I would appreciate any help as C++ is not my primary language.
I have a template class that is derived in multiple libraries. I am trying to figure out a way to uniq
You can do the following:
#include <iostream>
template <int id = 5>
class blah
{
public:
static const int cid = id;
};
int main(int argc, char *argv[])
{
std::cout << blah<>::cid << " " << blah<10>::cid << std::endl;
}
I don't know if it's a good idea though. The blah<>
part is a bit unintuitive too. Maybe you're better off assigning them ids manually, or you can create a base type, give it a template argument with your class id.
I'm not 100% happy with the answers so far and I have worked out one solution for me. The idea is to compute a hash of the type name using typeinfo. Everything is done once when loading the application so there's no runtime overload. This solution will work also using shared libraries as the type name and the hash of it will be consistent.
This is the code I use. This works great for me.
#include <string>
#include <typeinfo>
#include <stdint.h>
//###########################################################################
// Hash
//###########################################################################
#if __SIZEOF_POINTER__==8
inline uint64_t hash(const char *data, uint64_t len) {
uint64_t result = 14695981039346656037ul;
for (uint64_t index = 0; index < len; ++index)
{
result ^= (uint64_t)data[index];
result *= 1099511628211ul;
}
return result;
}
#else
inline uint32_t hash(const char *data, uint32_t len) {
uint32_t result = 2166136261u;
for (uint32_t index = 0; index < len; ++index)
{
result ^= (uint32_t)data[index];
result *= 16777619u;
}
return result;
}
#endif
inline size_t hash(const std::string & str) { return hash(str.c_str(), str.length()); }
//###########################################################################
// TypeId
//###########################################################################
typedef size_t TypeId;
template<typename T>
static const std::string & typeName() {
static const std::string tName( typeid(T).name() );
return tName;
}
template<typename T>
static TypeId typeId() {
static const TypeId tId = hash( typeName<T>() );
return tId;
}
In my previous company we did this by creating a macro which would take the class name as a parameter, create a local static with the unique id (based on class name) and then create an override of a virtual function declared in the base class that returned the static member. That way you can get the ID at runtime from any instance of the object hierarchy, similar to the 'getClass()' method in a java object, though much more primitive.
As Paul Houx pointed out here, trick with returning an address of static method may not work due to compiler optimizations. But we can make a workaround using __FILE__
and __LINE__
macros + volatile
keyword.
The final solution will look something like this:
#define GET_TYPE_ID static size_t GetTypeId() \
{ \
volatile const char* file = __FILE__; \
volatile uint32_t line = __LINE__; \
return (size_t)&GetTypeId; \
}
class ClassA
{
public:
GET_TYPE_ID
};
class ClassB
{
public:
GET_TYPE_ID
};
Here's what I ended up doing. If you have any feedback (pros, cons) please let me know.
template < class DERIVED >
class Foo
{
public:
static const char* name(); // Derived classes will implement, simply
// returning their class name
static int s_id()
{
static const int id = Id_factory::get_instance()->get_id(name());
return id;
}
// ...
};
Essentially the id will be assigned after doing a string comparison rather than a pointer comparison. This is not ideal in terms of speed, but I made the id static const so it will only have to calculate once for each DERIVED.