C++ 编译期间是否可以生成常量值?
我希望我的类通过一个唯一的哈希代码来标识每种类型。但我不希望每次在运行时调用一个方法(例如,C++ 编译期间是否可以生成常量值?,c++,visual-studio-2008,visual-c++,compiler-construction,C++,Visual Studio 2008,Visual C++,Compiler Construction,我希望我的类通过一个唯一的哈希代码来标识每种类型。但我不希望每次在运行时调用一个方法(例如,int GetHashCode())时都生成这些散列。我想使用已经生成的常量,我希望有一种方法可以让编译器进行一些计算并设置这些常量。可以使用模板来完成吗?如果可能的话,你能给我举个例子吗 更新: 感谢“评论”,我意识到我的问题应该是这样的: 如何以尽可能低的运行时成本进行类型检查? 我想根据类类型检查指向对象的指针。只是我在libs中实现的类,所以我考虑了一些自定义哈希,这就是最初的问题。我确实考虑过使
int GetHashCode()
)时都生成这些散列。我想使用已经生成的常量,我希望有一种方法可以让编译器进行一些计算并设置这些常量。可以使用模板来完成吗?如果可能的话,你能给我举个例子吗
更新:
感谢“评论”,我意识到我的问题应该是这样的:
如何以尽可能低的运行时成本进行类型检查?
我想根据类类型检查指向对象的指针。只是我在libs中实现的类,所以我考虑了一些自定义哈希,这就是最初的问题。我确实考虑过使用<代码> Type ID>代码>,但我不知道使用它的运行时成本。我做了一个假设,因为typeid生成了一个
type\u info
类,它比唯一int值的简单比较更消耗。您可以使用它。我会走一条简单的路线:
- 对于将是静态属性的类,只需为每个类选择一个数字
- 例如,只需使用地址即可
class MyClass
{
static const size_t TypeHashCode = typeid(MyClass).hash_code();
...
}
(我现在不熟悉编译器,因此这可能需要一些改进。我将尝试明天重新检查)
编辑:事实上,它不仅是微软特有的,而且只在VS2010中添加了——但是,嘿,至少微软同意这是一个有效的需求。如果您的代码中不同时允许VS2010和boost,那么您就只剩下符合标准的功能:typeid或dynamic_cast。它们确实会产生一些开销,但我会特别注意验证这一开销确实是一场有价值的战斗。(我的钱花在了-不是。)所有这些课程都有共同点。那么为什么不在一个公共枚举中为每个类添加一个符号常量呢?您可以让枚举为您提供值,这比提供显式常量容易(您仍然需要在枚举中声明每个类)。
模板
结构为类提供\u哈希\u代码\u
{
公众:
静态uintpttr_t GetHashCode()
{
返回(重新解释铸件(未使用)(&U));
}
私人:
静态空隙*未使用;
};
模板
void*为\u类::unused提供\u散列\u代码\u;
类MyClass:public为类提供\u散列\u代码\u
{
};
int main()
{
标准::库特尼古拉·费迪索夫在路线上建造:
- 对于将是静态属性的类,请使用转换为的函数的地址来提供唯一但已编译的值
- 例如,只需使用地址即可
标准不支持编译时类型的哈希代码,这是一个小问题。作为一种解决方法,可以从类名生成编译时哈希。下面是一个示例
#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;
}
}
}
#包括
#包括
#包括
#包括
#包括
#包括
//编译时字符串哈希。
类HashedString
{
公众:
typedef int64_t HashType;
显式constexpr HashedString(const char*str):m_hash(hashString(str)){
静态内联constexpr HashType hashString(const char*str)
{
返回(!str?0:hashStringRecursive(5381,str));
}
静态内联constexpr HashType hashStringRecursive(HashType hash,const char*str)
{
return(!*str?hash:hashStringRecursive((hash getId()){
案例SomeEvent1::id:
std::cout m_status可能是预编译器中的某个内容,但是没有关于预编译器哈希函数和/或随机数的具体想法。为什么您认为需要这样做?如果您获取hashcode()方法返回一个常量并内联。它不会在运行时真正被调用,但肯定会被编译器内联。没有运行时成本。不必费心寻找复杂的解决方法,只需信任编译器……在我的Symbian时代,我们使用编译后构建工具将各种ID注入ROM;我们喜欢简单的哈希预处理器本可以完成的系统,而不是试图跟踪占位符并将其外部化。您可以显示用于获取哈希代码的接口吗?GetHashCode是一个方法还是独立函数?您将已生成的常量保存在何处?在类内还是其他地方?我会在您的操作过程中处理实例检查说(某些情况除外),但为每个类选择一个数字对我来说不是一个选项。我正在开发3个可以单独或合作的库,我不想每次创建一个类时检查一个数字是否在所有3个库中都是唯一的。对不起,我不想使用boost。我应该在我的问题中提到这一点。使用枚举是我当前的解决方案,但我想改变我的想法因为为了满足我的需要,这样的枚举应该可以扩展到我的库之外。从MyClass派生的类具有与基相同的哈希代码,这是不可接受的。使用provide_hash_code_for_class会导致编译错误C2385:“GetHashCode”的访问不明确。这是一个很好的观点。您可能可以使用“Using provide_hash”来消除符号的歧义_在您正在编写的类中为_class::GetHashCode'编写代码,但这太多了……我想trait type类是不可能的,因为您可能不想更改接口?
#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;
}
}
}