+1  A: 

There is nothing standardized. Further, there's no hack that I've found that's foolproof.

Best I've been able to come up with:

template < class DERIVED, int sid >
class Foo
{
    public:    
      static int s_id()    
      {        
          return sid;
      }    
};

Foo<MyClass, 123456>   derivedObject;
James Curran
A: 

You can do the following:

#include <iostream>


template <int id = 5>
class blah
{
public:
    static const int cid = id; 
};

int main(int argc, char *argv[])
{
    std::cout << blah<>::cid << " " << blah<10>::cid << std::endl;

}

I don't know if it's a good idea though. The blah<> part is a bit unintuitive too. Maybe you're better off assigning them ids manually, or you can create a base type, give it a template argument with your class id.

Skurmedel
+1  A: 

In my previous company we did this by creating a macro which would take the class name as a parameter, create a local static with the unique id (based on class name) and then create an override of a virtual function declared in the base class that returned the static member. That way you can get the ID at runtime from any instance of the object hierarchy, similar to the 'getClass()' method in a java object, though much more primitive.

Jherico
That sounds like an excellent solution, but I'm not very familiar with macros.
xeon
+1  A: 

What kind of ID? Are you looking for an atomically increasing int? If a string is fine, what about:

static string s_id()
{
   return typeid(Foo<DERIVED>).name();
}

If it needs to be an int, but not automatically increasing, you could hash that for a 128-bit integer unlikely to have collisions (though likely a larger number than you need)

Todd Gardner
Argh, didn't see your answer before writing mine... +1 for speed.
Klaim
+1  A: 

In the modern C++ (03 - assuming you're using a recent compiler like gcc) you can use the typeid keyword to get a type_info object that provides basic type informations at least at runtime - that's a standard (and then cross-platform) feature.

I took the example from wikipedia and added a template/inheritance check, it seems to works well but i'm not certain for the int version (that is a hack exploiting the assumption that the compiler will have the types names somewhere in a read only memory space...that might be a wrong assumption).

The string identifier seems far better for cross-platform identification, if you can use it in you case. It's not cross-compiler compatible as the name it gives you is "implementation defined" by the standard - as suggested in comments.

The full test application code:

#include <iostream>
#include <typeinfo>  //for 'typeid' to work

class Person 
{
public:
   // ... Person members ...
   virtual ~Person() {}
};

class Employee : public Person 
{
   // ... Employee members ...
};

template< typename DERIVED >
class Test
{
public:
    static int s_id()
    {
        // return id unique for DERIVED
     // NOT SURE IT WILL BE REALLY UNIQUE FOR EACH CLASS!!
     static const int id = reinterpret_cast<int>(typeid( DERIVED ).name());
     return id;
    }

    static const char* s_name()
    {
        // return id unique for DERIVED
     // ALWAYS VALID BUT STRING, NOT INT - BUT VALID AND CROSS-PLATFORM/CROSS-VERSION COMPATBLE
     // AS FAR AS YOU KEEP THE CLASS NAME
     return typeid( DERIVED ).name();
    }
};

int wmain () 
{
    Person person;
    Employee employee;
    Person *ptr = &employee;



    std::cout << typeid(person).name() << std::endl;   // Person (statically known at compile-time)
    std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)
    std::cout << typeid(ptr).name() << std::endl;      // Person * (statically known at compile-time)
    std::cout << typeid(*ptr).name() << std::endl;     // Employee (looked up dynamically at run-time
                // because it is the dereference of a pointer to a polymorphic class)

    Test<int> test;
    std::cout << typeid(test).name() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_name() << std::endl;    

    Test< Person > test_person;
    std::cout << test_person.s_name() << std::endl;    
    std::cout << test_person.s_id() << std::endl;    

    Test< Employee > test_employee;
    std::cout << test_employee.s_name() << std::endl;    
    std::cout << test_employee.s_id() << std::endl;    

    Test< float > test_float;
    std::cout << test_float.s_name() << std::endl;    
    std::cout << test_float.s_id() << std::endl;    


    std::cin.ignore();
    return 0;
}

Outputs :

class Person
class Employee
class Person *
class Employee
class Test<int>
3462688
3462688
3462688
int
class Person
3421584
class Employee
3462504
float
3462872

This works at least on VC10Beta1 and VC9, should work on GCC. By the way, to use typeid (and dynamic_cast) you have to allow runtime type infos on your compiler. It should be on by default. On some plateform/compiler (I'm thinking about some embedded hardwares) RTTI is not turned on because it have a cost, so in some extreme cases you'll have to find a better solution.

Klaim
I like the reinterpret_cast of the pointer; hadn't thought of that. It should be unique for every class (technically, reinterpret cast doesn't guarantee that the ints should be separate, but guarantees reversibility, which would logically imply uniqueness)However, the string might not be cross platform; the name it gives you is "implementation defined" by the standard, and I have used platforms where it hands you back the mangled name.
Todd Gardner
Ah yes thanks for the precision, I fix that now.
Klaim
A: 
xeon