13

I've found a simple solution somewhere on the internet to an identity class without built-in C++ RTTI.

template <typename T>
class Identity {
public:
    static int64_t id()
    {
        static int64_t dummy;
        return reinterpret_cast<int64_t>(&dummy);
    }
};

When we need some class ID, we just use:

Identity<OurClass>::id();

I'm wondering, are there any collisions? Can it return the same ID for the different classes, or the different ID for the same classes? I have tried this code with g++ with different optimization values, everything seems ok.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
pproger
  • 347
  • 1
  • 2
  • 9

3 Answers3

12

First off: there is such an integral type that is made specifically to contain pointers:

  • intptr_t
  • and in C++11 uintptr_t

Second, even though in practice on gcc they are equal, the size of a pointer to an object and the size of a function pointer (or pointer to member) might well be different. Therefore it would be better using a specific object rather than the method itself (for Standard conformance).

Third, it only gives you identity, while RTTI is much richer, as it knows about all the subclasses a given object can be cast to, and even allows cross-casts or casts across virtual inheritance.

Still, the corrected version can be useful I guess:

struct Foo {
    static intptr_t Id() {
        static boost::none_t const Dummy = {};
        return reinterpret_cast<intptr_t>(&Dummy);
    }
};

And in hierarchies, having a virtual function returning that ID.

For completeness, I'll mention that Clang and LLVM have their own way of dealing with object identification without RTTI. You may want to read about their way of implementing isa, cast and dyn_cast here.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • thanks, but i didnt get, why the size of a pointer to an object and the size of a function pointer might well be different? can you give an example? – pproger Jun 17 '12 at 12:01
  • @pproger: The size of virtual member function pointers often differ in size from regular pointers. This is however compiler specific, you can read more about this in this excellent codeproject article: http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible – Tommy Andersen Jun 17 '12 at 12:14
  • @TommyA we dont have there any virtual functions. we got the pointer from static function – pproger Jun 17 '12 at 12:16
  • @pproger: the Standard leaves lee way to the compilers. Using VC++ a pointer to member function might be as 4 times as big as a regular pointer while a on gcc they always have the same size because gcc creates a "trampoline", ie a small (unnamed) function that performs the necessary adjustments before calling the real function you implemented. It's just something to keep in mind when portability is a concern. – Matthieu M. Jun 17 '12 at 12:22
0

This solution casts a function pointer to an int. There is no guarantee that this pointer fits into an int, although in practice sizeof(void *) == sizeof(void (*)()) <= sizeof(int)

Edit: My bad. On x86_64 sizeof(int) = 4, sizeof(void (*)()) = 8, so collisions are possible and are unpredictable.

You can cast to an integral of appropriate size, but still it is undefined behavior theoretically.

Asherah
  • 18,948
  • 5
  • 53
  • 72
KAction
  • 1,977
  • 15
  • 31
0

This version avoids undefined behavior (and compiler warnings):

template <typename T>
class Identity {
public:
    static const int* id() { static const int id = 0; return &id; }
};
Electro
  • 2,994
  • 5
  • 26
  • 32