riv
riv

Reputation: 7324

Generating unique type identifier at compile time

I need a way to generate a unique identifier (either string or integer) for any type for use in a set of template functions. The documentation for type_info::name() says it may or may not be different for different types, which worries me since I need to rely on them being unique. The hash_code probably isn't unique, either (judging by the name, I haven't looked at what the standard has to say on this).

My question mainly is, in what cases could typeid(T).name() be not unique?

Since types can come from different (DLL) modules, I can't simply use a global counter and a template static function. Thread safety isn't an issue though, since the functions that may require a type ID are guaranteed to only be called by one thread at a time.

Upvotes: 2

Views: 1801

Answers (2)

Ali
Ali

Reputation: 58451

I need a way to generate a unique identifier (either string or integer) for any type for use in a set of template functions.

Would something along these lines help?

#include <cstdint>
#include <iostream>
using namespace std;

template <typename T>
class unique_id {
  static char type_id;
public:
  static uintptr_t get_ID() { return reinterpret_cast<uintptr_t>(&type_id); }
};

template <typename T>
char unique_id<T>::type_id;

struct { } s;

int main() {
  cout << unique_id<int          >::get_ID() << endl;
  cout << unique_id<double       >::get_ID() << endl;
  cout << unique_id<decltype(s)  >::get_ID() << endl;
  cout << unique_id<size_t       >::get_ID() << endl;
  cout << unique_id<unsigned long>::get_ID() << endl; 
}

There is something similar in the LLVM codebase as it doesn't use RTTI. However, I have failed to find the description of that trick. Even 30 minutes of googling didn't help; I have given up.

My question mainly is, in what cases could typeid(T).name() be not unique?

I don't know the answer to that one. My guess is that the standard doesn't mandate that; of course, it doesn't answer the questions why.

OK, see Michael J's answer.


UPDATE: As for std::type_info::name(), it seems like there is no guarantee by the standard; the returned string can be identical for several types and change between invocations of the same program.

One option is to dive into implementation defined land: I would simply test it on the types I care about and on all platforms and compiler that I care about. If it works fine then I would probably risk it.

However, there are std::type_info::operator== and std::type_info::operator!= which may solve your problem: You can distinguish types from each other and it even seems to be guaranteed. Does it matter in your application whether you compare strings or type_info objects?

Upvotes: 3

Michael J
Michael J

Reputation: 7939

Sometimes more than one name can refer to the same type. Hopefully the following example will give you the idea

#include <typeinfo>
#include <iostream>

int main()
{
    class Foo
    {
    };
    class Bar : public Foo
    {
    };
    typedef int my_int;
    std::cout << "int8_t        = " << typeid(int8_t).name() << std::endl;
    std::cout << "int16_t       = " << typeid(int16_t).name() << std::endl;
    std::cout << "int32_t       = " << typeid(int32_t).name() << std::endl;
    std::cout << "int64_t       = " << typeid(int64_t).name() << std::endl;
    std::cout << "short         = " << typeid(short).name() << std::endl;
    std::cout << "short int     = " << typeid(short int).name() << std::endl;
    std::cout << "int           = " << typeid(int).name() << std::endl;
    std::cout << "long          = " << typeid(long).name() << std::endl;
    std::cout << "long int      = " << typeid(long int).name() << std::endl;
    std::cout << "long long     = " << typeid(long long).name() << std::endl;
    std::cout << "long long int = " << typeid(long long int).name() << std::endl;
    std::cout << "my_int        = " << typeid(my_int).name() << std::endl;
    std::cout << "Foo           = " << typeid(Foo).name() << std::endl;
    std::cout << "Bar           = " << typeid(Bar).name() << std::endl;

    return 0;
}

int8_t        = signed char
int16_t       = short
int32_t       = int
int64_t       = __int64
short         = short
short int     = short
int           = int
long          = long
long int      = long
long long     = __int64
long long int = __int64
my_int        = int
Foo           = ?AVFoo@?1?main@
Bar           = ?AVBar@?1?main@

Upvotes: 0

Related Questions