是否有可能在编译期间生成常量值?

时间:2010-06-14 20:19:35

标签: c++ visual-studio-2008 visual-c++ compiler-construction

我希望通过唯一的哈希码来识别每个类的类。但我不希望每次一个方法都产生这些哈希值,例如。 int GetHashCode(),在运行时调用。我想使用已经生成的常量,我希望有一种方法可以让编译器做一些计算并设置这些常量。可以使用模板完成吗?如果有可能的话,你能给我一些例子。

更新

感谢kriss'评论,我意识到我的问题应该是这样的: 如何以尽可能低的运行时间成本进行类型检查?

我想根据类类型检查指向对象的指针。只是我在我的库中实现的类,所以我想到了一些自定义哈希,因此是原始问题。我确实考虑过使用typeid,但我不知道使用它的运行时成本。我假设因为typeid产生的type_info类比单独的int值的简单比较更耗费。

7 个答案:

答案 0 :(得分:4)

您可以使用boost.MPL完成此操作。

答案 1 :(得分:2)

我会走简单路线:

  • 对于,这将是静态属性 - 所以只需为每个类选择一个数字。
  • 对于实例 - 只需使用地址。

答案 2 :(得分:1)

静态const在编译时进行评估 - 这几乎是元编程的基础。而且,type_info :: hash_code特别适合您的需求,所以试试 -

class MyClass
{
static const size_t TypeHashCode = typeid(MyClass).hash_code();
...
}

(我现在不在编译器周围,所以这可能需要一些改进。明天会尝试重新检查)

编辑:事实上,它不仅仅是MS特定的,而且还只在VS2010中添加 - 但是嘿,至少MS同意这是一个有效的需求。如果您不允许在代码中同时使用VS2010 ,那么您几乎可以使用标准兼容设施:typeid或dynamic_cast。他们确实会产生一些开销,但我会特别小心地确认这个开销确实是一场有价值的战斗。 (我的钱去了 - 不是。)

答案 3 :(得分:0)

所有这些类都有共同之处。那么为什么不在一个公共枚举中为每一个添加一个符号常量,你将保留enum给你的值,它比给出显式常量更容易(你仍然需要在枚举中声明每个类)。

答案 4 :(得分:0)

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);
}

请注意,哈希码在运行之间会发生变化,因此您不能依赖它们,例如进程间通信。

答案 5 :(得分:0)

建立在Nikolai N Fetissov的simple route路线上:

  • 对于属于静态属性的类 - 使用强制转换为intptr_t的函数的地址来提供唯一但已编译的值。
  • 对于实例 - 只需使用地址。

答案 6 :(得分: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;
        }
    }
}