Is it possible to implement type_id(T) in C++ which would not require manual type registration nor RTTI?
All the solutions I've seen (including boost::typeindex) are based on specialization and require manual "registration" like this:
class A {
public:
BOOST_TYPE_INDEX_REGISTER_CLASS
virtual ~A(){}
};
struct B: public A {
BOOST_TYPE_INDEX_REGISTER_CLASS
};
But I wish to be able to get type id for any arbitrary type including library types which I can't redefine in my code.
The typeid operator requires RunTime Type Identification (RTTI) to be generated, which must be explicitly specified at compile time through a compiler option.
Run-time type identification (RTTI) lets you find the exact type of an object when you have only a pointer or reference to the base type. This can be thought of as a “secondary” feature in C++, a pragmatism to help out when you get into messy situations.
RTTI, Run-Time Type Information, introduces a [mild] form of reflection for C++. It allows to know for example the type of a super class, hence allowing to handle an heterogeneous collection of objects which are all derived from the same base type. in ways that are specific to the individual super-classes.
RTTI is available only for classes that are polymorphic, which means they have at least one virtual method. In practice, this is not a limitation because base classes must have a virtual destructor to allow objects of derived classes to perform proper cleanup if they are deleted from a base pointer.
Generally the answer is "no". You can't implement fair type id without RTTI or specialization.
But there is one very powerful trick. It's non-obvious so it's uncommon in C++ world.
Every modern C++ compiler supports so-called function pretty-print macro, which allows you to get unique identifier of a function with all the type parameters unwrapped.
So you may use something like the code below:
#pragma once
#undef UNIQUE_FUNCTION_ID
#if defined(_MSC_VER)
#define UNIQUE_FUNCTION_ID __FUNCSIG__
#else
#if defined( __GNUG__ )
#define UNIQUE_FUNCTION_ID __PRETTY_FUNCTION__
#endif
#endif
template<typename T>
class TypeId
{
public:
static int typeId()
{
static int s_id = HASH( UNIQUE_FUNCTION_ID );
return s_id;
}
};
Where HASH
may be any good hash function you like.
Drawbacks:
constexpr
)T
and const T&
as different types (but it may be fixed with additional processing before hashing of UNIQUE_FUNCTION_ID
)Benefits:
I usually use pointer to function. Since every instantiation of a template function has a different address, I get a free hashing mechanism implemented by the compiler.
Here's how I do it:
using type_id_t = void(*)();
template<typename>
void type_id() {}
That's all! Now you can use it in maps like this:
std::map<type_id_t, std::string> myMap;
myMap[type_id<int>] = "a int";
myMap[type_id<SomeType>] = "some type";
CRTP idiom and a C-ish types system can help in this case:
#include<cstddef>
#include<cassert>
struct B {
static std::size_t cnt() noexcept {
static std::size_t val = 0;
return val++;
}
};
template<typename T>
struct I: private B {
static std::size_t type() noexcept {
static std::size_t t = B::cnt();
return t;
}
};
struct T: I<T> { };
struct S: I<S> { };
int main () {
assert(T::type() != S::type());
T t1, t2;
S s;
assert(t1.type() == t2.type());
assert(t1.type() != s.type());
}
You can use a macro as it follows too:
#define TypedStruct(C) struct C: I<C>
// ...
TypedStruct(T) { };
TypedStruct(S) { };
As mentioned in the comments, if you don't want it to interfere with your classes, you can use a similar approach as it follows:
#include<cstddef>
#include<cassert>
struct B {
static std::size_t cnt() noexcept {
static std::size_t val = 0;
return val++;
}
};
template<typename T>
struct Type: private B {
static const std::size_t type;
};
template<typename T>
const std::size_t Type<T>::type = B::cnt();
struct T { };
struct S { };
int main () {
assert(Type<T>::type != Type<S>::type);
}
As you can see, S
and T
are not affected by the Type
class.
The latter can be used at any time to give them an unique identifier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With