如何为唯一的 class 组合获得一致、唯一的标识符?

How can I get a consistent, unique, identifier for a unique class combination?

我需要一种方法来识别模板类型的唯一组合,从而为我提供易于索引的标识符。

我有以下 class:

#include <cstdint>

typedef std::uint32_t IDType;

template<class T>
class TypeIdGenerator
{
private:

    static IDType m_count;

public:

    template<class U>
    static IDType GetNewID()
    {
        static const IDType idCounter = m_count++;
        return idCounter;
    }
};

template<class T> IDType TypeIdGenerator<T>::m_count = 0;

这有效,但是当跨 dll 和 exe 使用时,每个进程中的每个“实例”都会重置计数,所以如果在 .dll 中我有:

IDType id = TypeIdGenerator<A>::GetNewID<B>();

与在 .exe 中使用相同的两个 classes:

进行的相同调用不同的值
IDType id = TypeIdGenerator<A>::GetNewID<B>();

当然,只有在不同 class 的类型以不同顺序生成时才会发生这种情况。

我知道静态成员在每个进程中都是静态的,而 dll 是另一个进程,那么我还能如何轻松检索一致的唯一 ID?

我更喜欢使用标准方法,没有特殊的编译器扩展或标志等。

编辑:关于我的用例的更多信息。

我有一个包含另一种类型的模板 class,因此我可以使用该模板类型对任意数据进行操作。非常简单:

class ComponentBase
{
public:
    virtual ~ComponentBase() {}

    virtual void DestroyData(unsigned char* data) const noexcept = 0;
    virtual void MoveData(unsigned char* source, unsigned char* destination) const = 0;
    virtual void ConstructData(unsigned char* data) const = 0;
};

template<class C>
class Component : public ComponentBase
{
public:
    typedef C type;

    virtual void DestroyData(unsigned char* data) const noexcept override
    {
        C* dataLocation = std::launder(reinterpret_cast<C*>(data));

        dataLocation->~C();
    }

    virtual void MoveData(unsigned char* source, unsigned char* destination) const override
    {
        new (&destination[0]) C(std::move(*reinterpret_cast<C*>(source)));
    }

    virtual void ConstructData(unsigned char* data) const override
    {
        new (&data[0]) C(*m_component);
    }
};

这是因为 AB 的实例需要并排存储在 unsigned char* 数组中。通过存储 std::unordered_map<IDType,ComponentBase*>s 的映射,我可以继续访问所有构造函数、析构函数、移动运算符等

我有一个容器,它为我提供了 unsigned char* 中哪个索引是 IDType 用于 AB 等的信息

因此,在对我的数据进行操作时,我会有一个 std::vector<IDType> 的容器,它从头到尾为我提供存储在 unsigned char* arbitrary.

中的每种类型

我可以使用 IDType 来查找我的 unordered_map<IDType, ComponentBase*>,这样我就可以对存储在 arbitrary 中的类型进行操作。

显然它比这更复杂一点,或者我只是存储 ComponentBase*s,但这是为什么我需要能够生成某些可索引类型的一致类型 ID 的一般要点。仅在我的程序的同一次执行期间保持一致,而不是在不同的执行之间保持一致(尽管那将是一个奖励)。

我正在使用通过 MSYS2 交付的 MingW64。

这取决于您创建的这些类型的数量,但我会寻找以下任一模式。

  1. ODR 和少量对象。
template<class T> IDType TypeIdGenerator<T>::m_count = 
                  (IDType)(void*)&TypeIdGenerator<T>::m_count;

这会在指向自身的指针的位置生成一个基数。它不能处理溢出到 2 个不同的 dll,但如果类型的数量很少,这应该使项目分开。

  1. 与单一来源进行一些交流。
IDType generator()
{
     static IDType m_count = 0;
     return m_count++;
}

所以在 class

template<class T>
class TypeIdGenerator
{
private:

static IDType m_count;

public:

    template<class U>
    static IDType GetNewID()
    {
        static const IDType idCounter = generate();
        return idCounter;
    }
};

由于 generator 是在单个二进制文件 (DLL) 中实现的,所有代码最终都将调用生成器来实现它,并且 Id 将是进程唯一的。

您可以使用散列函数生成 'unique' 散列,而不是增加计数器来创建类型 ID。虽然存在一定的碰撞机会,但如果哈希函数有效,则风险非常低。这种方法会在整个程序调用过程中为每种类型提供一致的 ID。

为此,C++ 实际上提供了一个 typeid operator which returns a type_info class instance. The type_info class implements a hash_code() 函数,它保证 return 每个唯一类型的唯一哈希值。

文档指出,此哈希值可能会在同一程序的调用之间发生变化,因此这不适用于多个不同的调用。

type_info class 也可以用来得到一个唯一的 type_index ,它可以直接用于索引到关联容器中。