独特的类类型ID,它是安全的并且跨库边界

时间:2009-05-28 18:22:58

标签: c++ class templates unique

我很感激任何帮助,因为C ++不是我的主要语言。

我有一个在多个库中派生的模板类。我试图找出一种方法来唯一地为每个派生类分配一个id int。我需要能够通过静态方法来实现,即。


template < class DERIVED >
class Foo
{
public:
    static int s_id()
    {
        // return id unique for DERIVED
    }
    // ...
};
谢谢!

11 个答案:

答案 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
};