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

xeo*_*eon 11 c++ templates class unique

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

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


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

Ben*_*igt 11

这可以用很少的代码完成:

template < class DERIVED >
class Foo
{
public:
    static int s_id()
    {
        return reinterpret_cast<int>(&s_id);
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 指针不能保证适合int.考虑可选类型uintptr_t或一些足够大小的实现定义的整数类型. (5认同)
  • 刚刚在Visual Studio 2015中试过这个,虽然它在调试模式下就像一个魅力,但由于优化,所有实现此技巧的类在Release模式下返回完全相同的id. (3认同)
  • 基本上,相同的函数折叠是一个好主意,但如果函数的地址被带到任何地方,优化器应该禁用它.Microsoft链接器不遵守该规则. (2认同)
  • @proski:我认为你误解了这个问题 - 这为每个类型提供了一个唯一的ID,而不是每个实例. (2认同)

Kla*_*aim 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;
}
Run Code Online (Sandbox Code Playgroud)

产出:

class Person
class Employee
class Person *
class Employee
class Test<int>
3462688
3462688
3462688
int
class Person
3421584
class Employee
3462504
float
3462872
Run Code Online (Sandbox Code Playgroud)

这至少在VC10Beta1和VC9上有效,应该适用于GCC.顺便说一句,要使用typeid(和dynamic_cast),您必须在编译器上允许运行时类型信息.默认情况下应该打开.在一些平台/编译器上(我正在考虑一些嵌入式硬件)RTTI没有打开因为它有成本,所以在某些极端情况下你必须找到一个更好的解决方案.

  • 我喜欢指针的reinterpret_cast; 没有想到这一点.它应该对每个类都是唯一的(从技术上讲,重新解释转换并不能保证int应该是单独的,但保证可逆性,这在逻辑上意味着唯一性)但是,字符串可能不是跨平台的; 它给你的名字是标准的"实现定义",我使用的平台可以将你的名字提交给你. (2认同)

xeo*_*eon 1

这就是我最终所做的。如果您有任何反馈(优点、缺点),请告诉我。


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; } // ... };
Run Code Online (Sandbox Code Playgroud)

本质上,id 将在进行字符串比较而不是指针比较后分配。这在速度方面并不理想,但我将 id 设置为静态常量,因此只需为每个 DERIVED 计算一次。