【问题标题】:Is it possible to generate constant value during compilation?编译期间是否可以生成常量值?
【发布时间】:2010-06-14 20:19:34
【问题描述】:

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

更新:

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

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

【问题讨论】:

  • 可能在预编译器中有一些东西,虽然没有关于预编译器哈希函数和/或随机的具体想法
  • 你认为你为什么需要这样做?
  • 如果您的 GetHashCode() 方法返回一个常量并被内联,它不会在运行时真正被调用,但肯定会被编译器内联。没有运行时成本。不要费心寻找复杂的解决方法,只要相信编译器......
  • 在我的 Symbian 时代,我们曾经使用编译后构建工具将各种 ID 注入到 ROM 中;我们会喜欢预处理器可以完成的简单哈希系统,而不是尝试跟踪占位符并将它们外部化
  • 你能显示你用来获取哈希码的界面吗? GetHashCode 是方法还是独立函数?您将已生成的常量保存在哪里 - 在类中或其他地方?

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


【解决方案1】:

您可以使用boost.MPL 来完成。

【讨论】:

  • 我不想使用 boost,抱歉。我应该在我的问题中提到这一点。
【解决方案2】:

我会走简单的路线:

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

【讨论】:

  • 我按照您的说法处理实例检查(某些情况除外),但是为每个类选择一个数字对我来说不是一个选项。我正在开发 3 个可以单独使用或合作的库,并且不想在每次创建类时检查一个数字在所有 3 个库中是否唯一。
【解决方案3】:

静态常量在编译时进行评估——这几乎是整个元编程的基础。此外,type_info::hash_code 特别适合你的需求,所以试试 -

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

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

编辑:确实,它不仅是特定于 MS 的,而且仅在 VS2010 中添加 - 但是,至少 MS 同意这是一个有效的需求。如果您不允许在代码中同时使用 VS2010 boost - 您几乎只能使用符合标准的工具:typeid 或 dynamic_cast。它们确实会产生一些开销,但我会格外小心地验证这种开销确实是一场有价值的战斗。 (我的钱去 - 不是。)

【讨论】:

    【解决方案4】:

    所有这些类都有一些共同点。那么为什么不在一个公共枚举中为每个枚举添加一个符号常量,你将让枚举为你提供值,这比提供显式常量更容易(你仍然需要在枚举中声明每个类)。

    【讨论】:

    • 使用枚举是我目前的解决方案,但我想改变它,因为为了满足我的需要,这样的枚举应该可以在我的库之外扩展。
    【解决方案5】:
    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);
    }
    

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

    【讨论】:

    • 从 MyClass 派生的类与基类具有相同的哈希码,这是不可接受的。使用 provide_hash_code_for_class 导致编译错误 C2385: 'GetHashCode' 的模糊访问。
    • 这很好。您可能可以在您正在编写的课程中使用 'using provide_hash_code_for_class::GetHashCode' 来消除符号的歧义,但是输入太多了...而且我猜 trait type class 是不可能的,因为您可能不想改变界面?
    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

      遗憾的是没有标准支持的编译时类型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;
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-31
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        • 2023-02-14
        • 1970-01-01
        • 2013-12-04
        • 2012-05-12
        相关资源
        最近更新 更多