Class identity without RTTI
Asked Answered
A

3

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.

Aiello answered 17/6, 2012 at 11:6 Comment(12)
In principle, yes. There's no guarantee that a function pointer is the same size as an int.Swaziland
This is relevant to my interests...Maure
To avoid the above problem, it would be better to put a static int variable in that static member function template and return a pointer to that. The compiler will optimize the function away anyway.Klutz
ok, if we assume, that pointer can be handled to int? i'll edit this code...Aiello
@pproger: Well, that's slightly better, but not well-defined C++. Just have a static int in your template.Klutz
@Electro: Why would that make a difference?Swaziland
@OliCharlesworth: See my previous previous comment. He should just return a pointer to a static int.Klutz
@Electro: I don't think it makes a difference. The compiler must supply a valid address for a function if one is requested, even if it's then inlined.Swaziland
@Klutz like this? but i dont see any ways, why previous solution is'nt workableAiello
@pproger: It's workable but undefined behavior.Klutz
@pproger: Please don't keep changing the code in your question. It potentially invalidates the answers that people have already given.Swaziland
@pproger: No, not like that. Check my (and Matthieu M.'s) answer below.Klutz
H
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.

Hobo answered 17/6, 2012 at 11:23 Comment(4)
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?Aiello
@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: codeproject.com/Articles/7150/…Merari
@TommyA we dont have there any virtual functions. we got the pointer from static functionAiello
@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.Hobo
T
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.

Tithable answered 17/6, 2012 at 11:11 Comment(1)
That's not always true in practice; try on x86-64.Swaziland
K
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; }
};
Klutz answered 17/6, 2012 at 11:23 Comment(1)
who knows. you may ask, why he doesnt use a native rtti, with the same result)Aiello

© 2022 - 2024 — McMap. All rights reserved.