14

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

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


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

4

11 回答 11

11

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

template < class DERIVED >
class Foo
{
public:
    static int s_id()
    {
        return reinterpret_cast<int>(&s_id);
    }
};
于 2011-10-07T19:44:03.710 回答
9

在现代 C++(03 - 假设您使用的是像 gcc 这样的最新编译器)中,您可以使用typeid关键字来获取至少在运行时提供基本类型信息的 type_info 对象 - 这是一个标准(然后是跨平台)功能.

我从 wikipedia 中获取了这个例子并添加了一个模板/继承检查,它似乎运行良好,但我不确定 int 版本(这是一个黑客利用编译器将在某个地方具有类型名称的假设)只读内存空间......这可能是一个错误的假设)。

字符串标识符对于跨平台识别似乎要好得多,如果您可以在您的情况下使用它。它不是交叉编译器兼容的,因为它给您的名称是标准的“实现定义” - 正如评论中所建议的那样。

完整的测试应用代码:

#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 没有打开,因为它有成本,所以在某些极端情况下,您必须找到更好的解决方案。

于 2009-05-28T22:26:42.893 回答
2

在我以前的公司中,我们通过创建一个宏来做到这一点,该宏将类名作为参数,创建一个具有唯一 id(基于类名)的本地静态,然后创建一个在基类中声明的虚函数的覆盖返回静态成员。这样,您可以在运行时从对象层次结构的任何实例中获取 ID,类似于 java 对象中的“getClass()”方法,但更原始。

于 2009-05-28T18:41:47.667 回答
2
#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());
}
于 2018-01-10T17:06:47.420 回答
2

到目前为止,我对答案并不是 100% 满意,我已经为我制定了一种解决方案。这个想法是使用 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;
}
于 2016-01-14T09:28:38.813 回答
1

什么样的身份证?你在寻找一个原子增加的 int 吗?如果字符串没问题,那又如何:

static string s_id()
{
   return typeid(Foo<DERIVED>).name();
}

如果它需要是一个 int,但不是自动增加,您可以将其散列为一个不太可能发生冲突的 128 位整数(尽管可能比您需要的数字更大)

于 2009-05-28T20:45:16.983 回答
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; } // ... };

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

于 2009-05-29T19:04:34.983 回答
1

下面的代码片段适用于 VS(2015) 和 Release 版本:

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) 上进行了尝试和测试。

它是如何工作的:TypeId<>模板的每个实例化都有它自己的、唯一的 sDummy 实例和它自己的、唯一的地址。老实说,我不完全确定为什么函数静态版本在发布时不起作用——我怀疑相同的 comdat 折叠和优化。

读者练习:至少 const 和 ref 类型应该获得与原始类型相同的类型 ID。

于 2019-03-31T06:13:05.857 回答
0

没有什么标准化的。此外,我发现没有任何破解方法是万无一失的。

我能想到的最好的:

template < class DERIVED, int sid >
class Foo
{
    public:    
      static int s_id()    
      {        
          return sid;
      }    
};

Foo<MyClass, 123456>   derivedObject;
于 2009-05-28T18:35:14.717 回答
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,或者你可以创建一个基本类型,给它一个带有你的类 id 的模板参数。

于 2009-05-28T18:38:06.973 回答
0

正如Paul Houx在这里指出的那样,由于编译器优化,返回静态方法地址的技巧可能不起作用。但是我们可以使用__FILE__and __LINE__macros +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
};
于 2020-07-16T14:48:04.143 回答