我很感激任何帮助,因为C ++不是我的主要语言。
我有一个在多个库中派生的模板类。我试图找出一种方法来唯一地为每个派生类分配一个id int。我需要能够通过静态方法来实现,即。
template < class DERIVED >
class Foo
{
public:
static int s_id()
{
// return id unique for DERIVED
}
// ...
};
谢谢!
答案 0 :(得分:11)
这可以用很少的代码完成:
template < class DERIVED >
class Foo
{
public:
static int s_id()
{
return reinterpret_cast<int>(&s_id);
}
};
答案 1 :(得分:8)
在现代C ++中(假设您正在使用像gcc这样的最新编译器),您可以使用typeid关键字来获取至少在运行时提供基本类型信息的type_info对象 - 这是标准(然后是跨平台)功能。
我从维基百科中获取了示例并添加了模板/继承检查,它似乎运行良好但我不确定int版本(这是一个hack利用编译器将在某处具有类型名称的假设只读存储空间......这可能是一个错误的假设)。
对于跨平台识别,字符串标识符似乎更好,如果您可以在您的情况下使用它。它不是交叉编译兼容的,因为它给你的名称是标准的“实现定义” - 正如评论中所建议的那样。
完整的测试应用程序代码:
#include <iostream>
#include <typeinfo> //for 'typeid' to work
class Person
{
public:
// ... Person members ...
virtual ~Person() {}
};
class Employee : public Person
{
// ... Employee members ...
};
template< typename DERIVED >
class Test
{
public:
static int s_id()
{
// return id unique for DERIVED
// NOT SURE IT WILL BE REALLY UNIQUE FOR EACH CLASS!!
static const int id = reinterpret_cast<int>(typeid( DERIVED ).name());
return id;
}
static const char* s_name()
{
// return id unique for DERIVED
// ALWAYS VALID BUT STRING, NOT INT - BUT VALID AND CROSS-PLATFORM/CROSS-VERSION COMPATBLE
// AS FAR AS YOU KEEP THE CLASS NAME
return typeid( DERIVED ).name();
}
};
int wmain ()
{
Person person;
Employee employee;
Person *ptr = &employee;
std::cout << typeid(person).name() << std::endl; // Person (statically known at compile-time)
std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)
std::cout << typeid(ptr).name() << std::endl; // Person * (statically known at compile-time)
std::cout << typeid(*ptr).name() << std::endl; // Employee (looked up dynamically at run-time
// because it is the dereference of a pointer to a polymorphic class)
Test<int> test;
std::cout << typeid(test).name() << std::endl;
std::cout << test.s_id() << std::endl;
std::cout << test.s_id() << std::endl;
std::cout << test.s_id() << std::endl;
std::cout << test.s_name() << std::endl;
Test< Person > test_person;
std::cout << test_person.s_name() << std::endl;
std::cout << test_person.s_id() << std::endl;
Test< Employee > test_employee;
std::cout << test_employee.s_name() << std::endl;
std::cout << test_employee.s_id() << std::endl;
Test< float > test_float;
std::cout << test_float.s_name() << std::endl;
std::cout << test_float.s_id() << std::endl;
std::cin.ignore();
return 0;
}
输出:
class Person
class Employee
class Person *
class Employee
class Test<int>
3462688
3462688
3462688
int
class Person
3421584
class Employee
3462504
float
3462872
至少在VC10Beta1和VC9上有效,应该适用于GCC。顺便说一句,要使用typeid(和dynamic_cast),您必须在编译器上允许运行时类型信息。默认情况下应该打开。在某些平台/编译器上(我正在考虑一些嵌入式硬件)RTTI没有开启因为它有成本,所以在某些极端情况下你必须找到更好的解决方案。
答案 2 :(得分:2)
在我以前的公司中,我们通过创建一个宏来实现这一点,该宏将类名作为参数,使用唯一的id(基于类名)创建一个本地静态,然后创建一个在返回静态成员的基类。这样,您可以在运行时从对象层次结构的任何实例获取ID,类似于java对象中的'getClass()'方法,尽管更原始。
答案 3 :(得分:2)
这就是我最终做的事情。如果您有任何反馈(优点,缺点),请告诉我。
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;
}
// ...
};
本质上,在进行字符串比较而不是指针比较之后,将分配id。这在速度方面并不理想,但我将id设为静态const,因此每个DERIVED只需计算一次。
答案 4 :(得分:1)
没有标准化的东西。此外,没有任何黑客,我发现这是万无一失的。
最好的我能够提出:
template < class DERIVED, int sid >
class Foo
{
public:
static int s_id()
{
return sid;
}
};
Foo<MyClass, 123456> derivedObject;
答案 5 :(得分:1)
什么样的身份证?你在寻找原子上增加的int吗?如果一个字符串没问题,那么:
static string s_id()
{
return typeid(Foo<DERIVED>).name();
}
如果它需要是一个int,但不是自动增加,你可以对一个不太可能发生冲突的128位整数进行哈希处理(虽然可能比你需要的数字更大)
答案 6 :(得分:1)
到目前为止,我对这些答案并不百分之百满意,而且我已经为我制定了一个解决方案。我们的想法是使用typeinfo计算类型名称的哈希值。加载应用程序时一切都完成,因此没有运行时超载。此解决方案也可以使用共享库作为类型名称,并且它的哈希值将保持一致。
这是我使用的代码。这对我很有用。
#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;
}
答案 7 :(得分:0)
您可以执行以下操作:
#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;
}
我不知道这是不是一个好主意。 blah<>
部分也有点不直观。也许你最好手动分配它们,或者你可以创建一个基类型,给它一个带有你的类id的模板参数。
答案 8 :(得分:0)
#include <stdint.h>
#include <stdio.h>
#define DEFINE_CLASS(class_name) \
class class_name { \
public: \
virtual uint32_t getID() { return hash(#class_name); } \
// djb2 hashing algorithm
uint32_t hash(const char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
DEFINE_CLASS(parentClass)
parentClass() {};
~parentClass() {};
};
DEFINE_CLASS(derivedClass : public parentClass)
derivedClass() : parentClass() {};
~derivedClass() {};
};
int main() {
parentClass parent;
derivedClass derived;
printf("parent id: %x\nderived id: %x\n", parent.getID(), derived.getID());
}
答案 9 :(得分:0)
以下代码段适用于VS(2015)和内部版本:
template <typename T>
struct TypeId
{
static size_t Get()
{
return reinterpret_cast<size_t>(&sDummy);
}
private:
static char sDummy;
};
template <typename T>
char TypeId<T>::sDummy; // don't care about value
还在GCC v7.3(Ubuntu 16.04)和LLVM v10.0.0(Mac OS High Sierra)上进行了测试。
供读者使用:至少const和ref类型应该具有与原始类型相同的类型ID。
答案 10 :(得分:0)
正如RT Patch指出的Paul Houx一样,由于编译器的优化,返回静态方法地址的技巧可能不起作用。但是我们可以使用__FILE__
和__LINE__
宏+ volatile
关键字来解决。
最终的解决方案如下所示:
#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
};