Reputation: 7324
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
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
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