【问题标题】:Assigning Unique Numerical Identifiers to Instances of a Templated Class为模板类的实例分配唯一的数字标识符
【发布时间】:2009-09-11 07:56:07
【问题描述】:

核心问题: 我希望能够获取模板类的实例,例如:

template<class a, class b, class c> class foo;

foo<int, float, double>;

然后执行以下操作:

foo<int, float, double>::value;  //Evaluates to a unique number
foo<long, float, double>::value; //Evaluates to a different unique number
foo<int, float, double>::value;  //Evaulates to the same unique number

除了,真的,它是:

template<class a, class b, class c>
int getUniqueIdentifier()
{
    return foo<a, b, c>::value;
} 

当前的解决方案尝试:
我在想我想使用 Boost::MPL 的“可扩展关联序列”,因为每个元素都有自己的唯一标识符,但我认为我需要能够就地更改序列,而“插入”没有这样做.
我可能会叫错树。 (从好的方面来说,dayum,但是 MPL!)

目的:
在信号和套接字系统上重新发明轮子。组件使用“总机”创建和注册通道,该总机将使用唯一标识符将通道放入映射中,从而实现运行时的多功能性。我尝试以 Qt 库为例,但我无法解析它们的缩写,而且我认为我缺少一些正式的专业知识。

谢谢!

【问题讨论】:

  • 这里的根本问题是模板可能在不同的翻译单元中被实例化。因此,不可能在编译时分配唯一的编号 - 两个 TU 可以在不同的计算机上同时编译!
  • 哦,核心问题似乎不是核心问题 - 有一个潜在的目的实际上可以通过其他方式更好地解决。

标签: c++ templates metaprogramming


【解决方案1】:

如果你想把东西放在地图中,并且需要一个每个类型的键,正确的解决方案是使用std::type_info::before()。派生一个类可能是值得的,这样您就可以提供operator&lt;,或者将std::type_info::before() 包装在一个二元谓词中。

【讨论】:

  • 好的,理解检查:当编译器编译一个模板类时,它会构建一组指令(各种函数)、一个人类可读的名称和一个机器可读的名称。这些东西中的每一个都存在于内存中,因此有一个内存地址,根据定义,每个模板“实例”都是唯一的,并且对于模板的所有相同“实例”都是相同的。 std::type_info,当您从它继承时,允许访问此信息吗?那么 std::typeid(class c) 是获取唯一标识符的简单方法吗?
  • 小心“定义独特”的声明。例如,模板函数可能是可内联的,因此这些函数的多个副本可以存在,内联在其他函数中。这在这里并不重要。要点是所有类型,包括类模板的实例(但不是模板本身)都有可用的 type_info ,并且它们具有编译器提供的顺序。此订单可通过type_info::before() 访问。从type_info 派生类的目的仅仅是调整其接口以用作std::map 中的键
【解决方案2】:

我只是碰巧在我的库中放置了这段骇客(uintxx 是我的 typedef,具有明显的含义)- 无需 rtti 即可工作。 32/64 兼容。前几个模板是定义一个包含 void* 的 pointer_uint

namespace pgast{

template <size_t size>
struct _pointer_uint{
};

template <>
struct _pointer_uint<2>{
    typedef uint16 intrinsic;
};

template <>
struct _pointer_uint<3>{
    typedef uint32 intrinsic;
};

template <>
struct _pointer_uint<4>{
    typedef uint32 intrinsic;
};

template <>
struct _pointer_uint<5>{
    typedef uint64 intrinsic;
};

template <>
struct _pointer_uint<6>{
    typedef uint64 intrinsic;
};

template <>
struct _pointer_uint<7>{
    typedef uint64 intrinsic;
};

template <>
struct _pointer_uint<8>{
    typedef uint64 intrinsic;
};

typedef _pointer_uint< sizeof(void*) >::intrinsic pointer_uint; 

template <class c>
struct Class_Identifier{
   static pointer_uint id(){
       static char _id;
       return reinterpret_cast<pointer_uint>(&_id);
   }
   template <class c2> 
   bool operator==(const Class_Identifier<c2>& rhs)const{
      return id() == Class_Identifier<c2>::id();
   }
   template <class c2> 
   bool operator<(const Class_Identifier<c2>& rhs)const{
      return id() < Class_Identifier<c2>::id();
   }
};

}//namespace pgast

/*
Copyright (c)1993,2001 J. E. Pendergast Jr. 
*/

【讨论】:

    【解决方案3】:

    很有可能发生碰撞,但为了简单起见,您无法击败:

    template<class a, class b, class c>
    static int getUniqueIdentifier()
    {
        return sizeof( a ) + sizeof( b ) + sizeof( c );
    }
    

    【讨论】:

      【解决方案4】:

      如果你启用了 rtti,你可以连接类的名字,你的模板被实例化,然后返回这个字符串的哈希值。通过选择一个好的散列函数,你可以最大限度地减少冲突的机会(也许你也不需要一个 rtti,你可以引入一些你自己的 rtti):

      // basic template for gaining names of the classes:
      template<typename T>
      class Rtti
      {
      static std::string GetRTTIName() { return std::string(); }
      };
      
      // specialization for the classes, you expect to use:
      template<>
      class Rtti<float>
      {
      static std::string GetRTTIName() { return std::string("float"); }
      };
      

      【讨论】:

      • std::type_info 完全没问题。但是(我认为)它可以被项目设置禁用......
      • 它可以在某些编译器上被禁用。但这很愚蠢,禁用了您正在使用的标准 C++ 功能。
      【解决方案5】:

      如果您在一个头文件中完成这一切,您可以使用__LINE__ 宏来获取一些唯一编号。但是,一旦将其分布在多个文件中,它们就不再是唯一的了。

      IIRC,VC 有一些宏,每次使用宏时都会计算一个数字。因此,如果您仅使用 VC,则可以使用它。但是,这也仅适用于一个翻译单元。

      除此之外,我不知道如何从类型中获取唯一数字。

      但是,为什么不使用typeid 作为地图的键类型?像这样的:

      typedef std::map< std::type_info, whatever > my_map;
      
      template< typename a, typename b, typename c>
      void register_foo(my_map& the_map, const foo<a,b,c>& some_bar, whatever the_whatever) 
      {
        the_map.insert( typeid(foo<a,b,c>), the_whatever );
      }
      

      【讨论】:

        【解决方案6】:

        或者怎么样:

        template<class a, class b, class c, int uniqueId>
        class Test
        {
        public:
            int uid() {
                return uniqueId;
            }
        };
        
        int main(int argc, char* argv[])
        {
            Test<int, int, int, 5> test1;
        
            std::cout << test1.uid() << std::endl;
        
            return 0;
        }
        

        如果您希望所有具有相同模板参数的实例具有相同的 id,您可以使用;

        template<class a, class b, class c>
        class TestId5 : public Test<a, b, c, 5>{};
        

        【讨论】:

          【解决方案7】:

          您想要每个实例还是每个实例化?为以后

          template <...>
          class foo 
          {
          public:
             static unsigned id;
          };
          
          extern unsigned fooCounter;
          
          template <...>
          unsigned foo::id = ++fooCounter;
          

          甚至

          template <...>
          unsigned foo::id = reinterpret_cast<unsigned>(&id);
          

          【讨论】:

          • 我相当肯定后者是未定义的——根据 ODR,常量初始化器在所有翻译单元中应该具有相同的值。
          • @MSalters,后者中的初始化器不再是一个常量表达式,而不是前者。而且我刚刚重读了 3.2,特别是 3.2/5,并没有看到任何阻止后者被明确定义的东西。 (reinterpret_cast 将无法在 unsigned 不足以包含指针的实现上编译,但这是另一个问题)。
          • 似乎你可能是对的 - 3.2/5 提到(在它进入具有内部或没有链接的 const 对象名称的例外之前)名称在任何地方都指代相同实体的可能性。现在,问题是,这会调用循环逻辑:如果 ODR 适用,&amp;id 在任何地方都是相同的名称,并且 ODR 适用。但如果 ODR 不适用,&amp;id 在所有地方都不是同一个名称,并且 ODR 不适用。嗯……博士?
          【解决方案8】:

          重新审视这个问题;在我看来,如果我可以保证模板实例化中的某些函数对于该实例化是唯一的,我可以简单地使用该函数的地址作为 ID。

          当我意识到我不必使用 type_id 中的实际字符串时,我想到了这一点,我可以只使用字符串的位置。

          例如;

          template<class a>
          foo
          {
              void id(){}
              ...
          }
          

          &foo<int>::id != &foo<float>::id  //?
          

          如果这是真的,那么我可以将其用作地图的每个专业的唯一 ID 号,而不依赖于 RTTI。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-07-24
            • 1970-01-01
            • 1970-01-01
            • 2019-12-01
            • 1970-01-01
            相关资源
            最近更新 更多