如何为唯一的 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);
}
};
这是因为 A
或 B
的实例需要并排存储在 unsigned char*
数组中。通过存储 std::unordered_map<IDType,ComponentBase*>
s 的映射,我可以继续访问所有构造函数、析构函数、移动运算符等
我有一个容器,它为我提供了 unsigned char*
中哪个索引是 IDType
用于 A
或 B
等的信息
因此,在对我的数据进行操作时,我会有一个 std::vector<IDType>
的容器,它从头到尾为我提供存储在 unsigned char* arbitrary
.
中的每种类型
我可以使用 IDType 来查找我的 unordered_map<IDType, ComponentBase*>
,这样我就可以对存储在 arbitrary
中的类型进行操作。
显然它比这更复杂一点,或者我只是存储 ComponentBase*
s,但这是为什么我需要能够生成某些可索引类型的一致类型 ID 的一般要点。仅在我的程序的同一次执行期间保持一致,而不是在不同的执行之间保持一致(尽管那将是一个奖励)。
我正在使用通过 MSYS2 交付的 MingW64。
这取决于您创建的这些类型的数量,但我会寻找以下任一模式。
- ODR 和少量对象。
template<class T> IDType TypeIdGenerator<T>::m_count =
(IDType)(void*)&TypeIdGenerator<T>::m_count;
这会在指向自身的指针的位置生成一个基数。它不能处理溢出到 2 个不同的 dll,但如果类型的数量很少,这应该使项目分开。
- 与单一来源进行一些交流。
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 ,它可以直接用于索引到关联容器中。
我需要一种方法来识别模板类型的唯一组合,从而为我提供易于索引的标识符。
我有以下 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);
}
};
这是因为 A
或 B
的实例需要并排存储在 unsigned char*
数组中。通过存储 std::unordered_map<IDType,ComponentBase*>
s 的映射,我可以继续访问所有构造函数、析构函数、移动运算符等
我有一个容器,它为我提供了 unsigned char*
中哪个索引是 IDType
用于 A
或 B
等的信息
因此,在对我的数据进行操作时,我会有一个 std::vector<IDType>
的容器,它从头到尾为我提供存储在 unsigned char* arbitrary
.
我可以使用 IDType 来查找我的 unordered_map<IDType, ComponentBase*>
,这样我就可以对存储在 arbitrary
中的类型进行操作。
显然它比这更复杂一点,或者我只是存储 ComponentBase*
s,但这是为什么我需要能够生成某些可索引类型的一致类型 ID 的一般要点。仅在我的程序的同一次执行期间保持一致,而不是在不同的执行之间保持一致(尽管那将是一个奖励)。
我正在使用通过 MSYS2 交付的 MingW64。
这取决于您创建的这些类型的数量,但我会寻找以下任一模式。
- ODR 和少量对象。
template<class T> IDType TypeIdGenerator<T>::m_count =
(IDType)(void*)&TypeIdGenerator<T>::m_count;
这会在指向自身的指针的位置生成一个基数。它不能处理溢出到 2 个不同的 dll,但如果类型的数量很少,这应该使项目分开。
- 与单一来源进行一些交流。
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 ,它可以直接用于索引到关联容器中。