我希望我的类能够通过唯一的哈希码来识别每种类型。但我不希望每次使用方法时都生成这些哈希值,例如。
int GetHashCode()
,在运行时调用。我想使用已经生成的常量,并且希望有一种方法可以让编译器进行一些计算并设置这些常量。可以使用模板来完成吗?如果可以的话,你能给我举个例子吗?
更新:
感谢kriss'评论,我意识到我的问题应该是这样的: 如何以尽可能低的运行成本进行类型检查?
我想根据类类型检查指向对象的指针。只是我在库中实现的类,所以我正在考虑一些自定义哈希,因此是最初的问题。我确实考虑过使用
typeid
但我不知道使用它的运行时成本。我做了一个假设,因为 typeid 生成一个 type_info
类,它比简单比较唯一 int 值更消耗。
boost.MPL
来完成。
我会走简单的路线:
静态常量在编译时进行评估 - 这几乎是整个元编程的基础。此外,type_info::hash_code 特别适合您的需求,所以尝试 -
class MyClass
{
static const size_t TypeHashCode = typeid(MyClass).hash_code();
...
}
(我现在不在编译器周围,所以这可能需要一些改进。明天会尝试重新检查)
编辑:确实,它不仅是 MS 特有的,而且仅在 VS2010 中添加 - 但是嘿,至少 MS 同意这是一个有效的需求。如果您不允许在代码中同时使用 VS2010 和 boost - 您几乎只剩下符合标准的设施:typeid 或dynamic_cast。 它们确实会产生一些开销,但我会格外小心地验证这些开销确实是一场值得的战斗。 (我的钱去 - 不。)
所有这些类都有一些共同点。那么为什么不在公共枚举中为每个类添加一个符号常量,您将让枚举为您提供值,这比给出显式常量更容易(您仍然需要在枚举中声明每个类)。
template<class T>
struct provide_hash_code_for_class
{
public:
static uintptr_t GetHashCode()
{
return(reinterpret_cast<uintptr_t>(&unused));
}
private:
static void *unused;
};
template<class T>
void *provide_hash_code_for_class<T>::unused;
class MyClass : public provide_hash_code_for_class<MyClass>
{
};
int main()
{
std::cout << std::hex << MyClass::GetHashCode() << std::endl;
std::cout << std::hex << MyClass().GetHashCode() << std::endl;
return(0);
}
请注意,哈希代码会在运行之间发生变化,因此您不能依赖它们来进行进程间通信等。
遗憾的是标准没有支持编译时类型hash_code。作为一种解决方法,可以从类名生成编译时哈希。下面是一个例子。
#include <stdint.h>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <cassert>
//Compile-time string hashing.
class HashedString
{
public:
typedef int64_t HashType;
explicit constexpr HashedString(const char* str): m_hash(hashString(str)) {}
static inline constexpr HashType hashString(const char* str)
{
return ( !str ? 0 : hashStringRecursive(5381, str));
}
static inline constexpr HashType hashStringRecursive(HashType hash, const char* str)
{
return ( !*str ? hash : hashStringRecursive(((hash << 5) + hash) + *str, str + 1));
}
const HashType m_hash;
};
struct EventBase
{
using IdType = HashedString::HashType;
virtual ~EventBase() {}
IdType getId() const { return m_eventId; } //present the runtime event id
EventBase(IdType myId) : m_eventId { myId } { }
template<class DerivedEvent>
const DerivedEvent* getAs() const
{
return dynamic_cast<const DerivedEvent*>(this);
}
protected:
const IdType m_eventId;
};
#define DEFINE_EVENT_ID(className) \
static constexpr IdType id = HashedString(#className).m_hash; \
struct SomeEvent1 : public EventBase
{
DEFINE_EVENT_ID(SomeEvent1);
SomeEvent1(int status) : EventBase(id), m_status { status } { assert(id == m_eventId); }
int m_status;
};
struct SomeEvent2 : public EventBase
{
DEFINE_EVENT_ID(SomeEvent2);
SomeEvent2() : EventBase(id) { assert(id == m_eventId); }
std::string m_s = "test event 2";
};
void testEvents()
{
std::vector<std::shared_ptr<EventBase>> events;
events.push_back(std::make_shared<SomeEvent1>(123));
events.push_back(std::make_shared<SomeEvent2>());
for (auto event : events) {
switch(event->getId()) {
case SomeEvent1::id:
std::cout << "SomeEvent1 " << event->getAs<SomeEvent1>()->m_status << std::endl;
break;
case SomeEvent2::id:
std::cout << "SomeEvent2 " << event->getAs<SomeEvent2>()->m_s << std::endl;
break;
}
}
}