简体   繁体   English

如何在编译时生成密集的唯一类型ID?

[英]How can I generate dense unique type IDs at compile time?

I'm trying to make a system of classes that are small objects, and the base class has a member that is a unique identifier that identifies the class: 我正在尝试创建一个小型对象的类系统,并且基类的成员是标识该类的唯一标识符:

class Shape
{
public:
    unsigned char id;
};

template <typename T>
class Triangle : public Shape
{
    T triangle_data;
};

template <typename T>
class Square : public Shape
{
    T square_data;
};

template <typename T>
class ShapeBox : public Shape
{
    T shapebox_data;
    Shape * child_shape;
};

With the class identifier, I go through a vector of Shape * and switch on the id visible in the base class, then static cast for different behavior (to Triangle, Square, or ShapeBox and child shapes held in it respectively for the example class hierarchy) 使用类标识符,我通过Shape *的向量并切换基类中可见的id,然后针对不同的行为进行静态转换(分别为示例类层次结构中的Triangle,Square或ShapeBox和子形状) )

I could turn on RTTI, but the space cost seems fairly large, especially when the type information can be implemented as a pointer and the object size might be no bigger than a couple of bytes. 我可以打开RTTI,但是空间成本似乎相当大,特别是当类型信息可以实现为指针并且对象大小可能不大于几个字节时。 There may be millions of small objects, and I really only need static cast anyways. 可能有数百万个小物件,我真的只需要静态铸造。

Currently I can make type identifiers by using statics that are assigned values from a static monotonically incrementing counter: 目前,我可以使用从静态单调递增计数器分配值的静态来创建类型标识符:

class TypeID
{
    static size_t counter;

public:
    template<typename T>
    static size_t value()
    {
        static size_t id = counter++;
        return id;
    }
};
size_t TypeID::counter = 1;

Ideally I want dense, unique type ID's that are available at compile time, so the compiler can perform well, like converting a switch on the type IDs into a constant time jump table, or at least a binary search tree rather than a linear time if/else chain for what might be a long list of type IDs... 理想情况下,我需要在编译时可用的密集,唯一类型ID,因此编译器可以很好地执行,例如将类型ID上的开关转换为常量时间跳转表,或者至少是二进制搜索树而不是线性时间/ else链可能是一长串的类型ID列表...

I can use boilerplate at compile time to manually assign every type ID, or I can use object/function pointers from a similar type ID class. 我可以在编译时使用样板来手动分配每个类型ID,或者我可以使用来自类似类型ID类的对象/函数指针。 Boiler plate is guaranteed to be dense (because we assign it manually) and known at compile time, but it's unmaintainable for template types. 锅炉板保证密集(因为我们手动分配)并且在编译时已知,但它对于模板类型是不可维护的。 Whenever you add a template type to a shape, you have to manually add a new type. 每当向模板添加模板类型时,都必须手动添加新类型。 The monotonic static counter is maintainable and dense, but unknown at compile time and so compile time optimizations aren't possible, and thread safety may be a concern. 单调静态计数器是可维护和密集的,但在编译时是未知的,因此编译时优化是不可能的,并且线程安全可能是一个问题。 The function pointer ID is known at compile time and maintainable, but isn't dense and won't fit into a small id type like a char. 函数指针ID在编译时是已知的并且是可维护的,但是不是密集的,并且不适合像char这样的小id类型。

Is there any way to generate type IDs that are visible to the compiler at compile time, dense, and automatically assigned, perhaps using template metaprogramming counter or some preprocessor magic in C++11 or C++14? 有没有办法生成编译时编译器可见,密集和自动分配的类型ID,可能使用模板元编程计数器或C ++ 11或C ++ 14中的一些预处理器魔术? Or is this not possible until C++ has compile time reflection? 或者这是不可能的,直到C ++有编译时反射?

Is there any way to generate type IDs that are visible to the compiler at compile time, dense, and automatically assigned, perhaps using template metaprogramming counter 有没有办法生成编译时编译器可见,密集和自动分配的类型ID,可能使用模板元编程计数器

I've developed a code which does this with few restrictions. 我已经开发了一个代码来实现这一点,但没有什么限制。 Two template specializations ID_by_T and T_by_ID define type <=> ID link at compile time. 两个模板特化ID_by_TT_by_ID在编译时定义type <=> ID链接。 Type's ID is a enum constant. Type的ID是枚举常量。 If type <=> ID link is not defined ID_by_T<type>::ID is -1 and T_by_ID<undefinedID>::type is null_t predefined type. 如果未定义type <=> ID链接ID_by_T<type>::ID-1T_by_ID<undefinedID>::typenull_t预定义类型。 DEF_TYPE_ID(type_name) macro generates new ID when defines type <=> ID link. DEF_TYPE_ID(type_name)宏在定义type <=> ID链接时生成新ID。 int_triangle and char_triangle show how to get typedef with correct type's ID and inner typedef _MyID_T shows how to get ID of type. int_trianglechar_triangle显示如何获取具有正确类型ID的typedef和内部typedef _MyID_T显示如何获取类型的ID。 The code was compiled with MS VS 2005 C++. 代码是使用MS VS 2005 C ++编译的。

The core -- type_id_map header file: 核心 - type_id_map头文件:

#ifndef __TYPE_ID_MAP__
#define __TYPE_ID_MAP__

namespace ns_type_ids {
    struct null_t {};
    template<class T, int ID_v>
    struct ID_T_pair {
        enum {ID=ID_v};
        typedef T _Type;
        inline static const _TCHAR * name() { return _T("unknown"); }
    };

    template<class T> struct ID_by_T: ID_T_pair<T, -1> {};
    template<int ID_v> struct T_by_ID: ID_T_pair<null_t, ID_v> {};

    template<> struct ID_by_T<null_t>: ID_T_pair<null_t, -1> {
        inline static const _TCHAR * name() { return _T("null_t"); }
    };
    template<> struct T_by_ID<ID_by_T<null_t>::ID>: ID_by_T<null_t> {};
};

#define PREV_TYPE null_t
#define DEF_TYPE_ID(type_name) \
namespace ns_type_ids { \
    template<> struct ID_by_T<type_name>: ID_T_pair<type_name, ID_by_T<PREV_TYPE>::ID+1> { \
        inline static const _TCHAR * name() { return _T(#type_name); } \
    }; \
    template<> struct T_by_ID<ID_by_T<type_name>::ID>: ID_by_T<type_name> {}; \
};

#endif

And the use of type_id_map example in templated_cls_id.cpp : 并在templated_cls_id.cpp中使用type_id_map示例:

#include <tchar.h>
#include <iostream>

#ifdef _UNICODE
#define _tcout wcout
#else
#define _tcout cout
#endif


#include "type_id_map"    

//targeted types
struct shape {};

template<class T>
struct data_t: shape {
    T _data;
};

template<class T>
struct triangle: data_t<T>, ns_type_ids::ID_by_T<data_t<T> > {
    typedef data_t<T> _MyID_T;
};

//begin type <=> id map
DEF_TYPE_ID(int)
#undef  PREV_TYPE
#define PREV_TYPE int

DEF_TYPE_ID(double)
#undef  PREV_TYPE
#define PREV_TYPE double

DEF_TYPE_ID(float)
#undef  PREV_TYPE
#define PREV_TYPE float

DEF_TYPE_ID(data_t<int>)
#undef  PREV_TYPE
#define PREV_TYPE data_t<int>

DEF_TYPE_ID(data_t<char>)
#undef  PREV_TYPE
#define PREV_TYPE data_t<char>
//end type <=> id map

//Now targeted classes could be defined
typedef triangle<int> int_triangle;
typedef triangle<char> char_triangle;

int _tmain(int argc, _TCHAR* argv[]) {
    using namespace std;
    using namespace ns_type_ids;
#define out_id(type_name) \
    _T("ID_by_T<") _T(#type_name) _T(">::ID: ") << ID_by_T<type_name>::ID
#define out_name(type_id) \
    _T("T_by_ID<") _T(#type_id) _T(">: ") << T_by_ID<type_id>::name()

    _tcout
        << out_id(null_t) << endl
        << out_id(int) << endl
        << out_id(double) << endl
        << out_id(float) << endl
        << out_id(int_triangle::_MyID_T) << endl
        << out_id(char_triangle::_MyID_T) << endl

        << out_name(-1) << endl
        << out_name(0) << endl
        << out_name(1) << endl
        << out_name(2) << endl
        << out_name(3) << endl
        << out_name(4) << endl
    ;
    return 0;
#undef out_id
#undef out_name
}

Output: 输出:

ID_by_T<null_t>::ID: -1
ID_by_T<int>::ID: 0
ID_by_T<double>::ID: 1
ID_by_T<float>::ID: 2
ID_by_T<int_triangle::_MyID_T>::ID: 3
ID_by_T<char_triangle::_MyID_T>::ID: 4
T_by_ID<-1>: null_t
T_by_ID<0>: int
T_by_ID<1>: double
T_by_ID<2>: float
T_by_ID<3>: data_t<int>
T_by_ID<4>: data_t<char>

Requirements and restrictions: 要求和限制:

  1. Type <=> ID map is global and works only at compile time. Type <=> ID映射是全局的,仅在编译时有效。
  2. Type <=> ID link must be defined at global namespace level using DEF_TYPE_ID and PREV_TYPE macro. 必须使用DEF_TYPE_IDPREV_TYPE宏在全局命名空间级别定义Type <=> ID链接。
  3. "IDed" type must be declared prior to defintion of Type <=> ID link. 必须在定义Type <=> ID链接之前声明“IDed”类型。
  4. In order to get self ID inside a class use ID_by_T<self_type> as a base class where self_type is an own type of class. 为了在类中获取自身ID,请使用ID_by_T<self_type>作为基类,其中self_type是自己的类类型。 But why (see below)? 但为什么(见下文)?
  5. In order to get self ID inside a templated class use ID_by_T<base_data_type> as a base class where base_data_type is a special base type of templated class for wich type <=> ID link is already defined. 为了在模板化类中获取自身ID,请使用ID_by_T<base_data_type>作为基类,其中base_data_type是用于类型的模板化类的特殊基type <=> ID链接已经定义。 See int_triangle and char_triangle for example. 例如,请参见int_trianglechar_triangle Also there are other tricks to get defined ID inside a template instance. 还有其他技巧可以在模板实例中获取定义的ID。

Features 特征

  • IDs are external and allocated at compile time automatically sequentially beginning with 0 in the compilation order of type <=> ID link definitions. ID是外部的,并在编译时自动按顺序从0开始按type <=> ID链接定义的编译顺序分配 This is inevitability due to the requirement of the question. 由于问题的要求,这是不可避免的。
  • Minimal requirements to a compiler: only standard features of ISO/IEC 14882:2003 SE . 对编译器的最低要求:仅ISO/IEC 14882:2003 SE标准功能。
  • Macros __COUNTER__ , __LINE__ , BOOST_PP_COUNTER or based on sizeof are not used to allocate ID: there are no side effects associated with them. __COUNTER____LINE__BOOST_PP_COUNTER或基于sizeof不用于分配ID:有与它们相关联无副作用。
  • The type <=> id map is based on external IDs known at compile time: type <=> id映射基于编译时已知的外部ID:
    • An ID can be assigned to every type, even to a fundamental type. 可以为每种类型分配ID,甚至可以分配给基本类型。
    • ID_T_pair template describes type <=> id link. ID_T_pair模板描述了type <=> id链接。 ID_by_T / T_by_ID templates are direct descendants of ID_T_pair template. ID_by_T / T_by_ID模板的直接后裔ID_T_pair模板。
    • Due to ID_by_T template it is not necessary to define ID inside of a type (what is impossible for fundamental types). 由于ID_by_T模板,没有必要在类型内部定义ID(基本类型不可能)。
    • ID by type is accessed with ID_by_T<type>::ID enum constant. 使用ID_by_T<type>::ID枚举常量访问ID类型。
    • Type by ID is accessed with T_by_ID<ID>::_Type inner typedef. 使用T_by_ID<ID>::_Type内部typedef访问T_by_ID<ID>::_Type
    • Optional: type's name is accessed with name() method of ID_T_pair . 可选:使用ID_T_pair name()方法访问类型的名称。
    • The map does not occupy any memory byte if method name() of ID_T_pair is not used. 如果未使用ID_T_pair方法name() ,则映射不会占用任何内存字节。
  • The map is distributed: type <=> id link can be defined in place but at global namespace level. 映射是分布式的: type <=> id link可以在适当的位置定义,但在全局命名空间级别定义。
  • In order to access the map in a few TU a special header may be used. 为了在几个TU中访问地图,可以使用特殊标题。
  • Definition of a composed derived type does not require any additional info for the map. 组合派生类型的定义不需要地图的任何其他信息。
  • The map uses special null type null_t and ID=-1 returned in absence of type <=> ID link. 在没有type <=> ID链接的情况下,映射使用特殊的null类型null_tID=-1

Today I've developed another solution to assign ID for every template instance automatically without the need to define an alias for every "IDed" template instance. 今天,我开发了另一种解决方案,可以自动为每个模板实例分配ID,而无需为每个“IDed”模板实例定义别名。 The solution named v2 is based on previous referred as v1. 名为v2的解决方案基于之前称为v1的解决方案。 The v1's feature of assigning an ID to a fundamental type is required to automatically assign unique ID to every template instance. v1为基本类型分配ID的功能是自动为每个模板实例分配唯一ID。

UPD: important note on the choosing of the only ID allocator UPD:关于选择唯一ID分配器的重要说明

The problem addressed here is related to the task but both answers. 这里解决的问题与任务有关,但两个答案都有关。 Approaches of the task implies the only ID allocator due to requirements: 由于要求,该任务的方法意味着唯一的ID分配器:

I could turn on RTTI, but the space cost seems fairly large, especially when the type information can be implemented as a pointer and the object size might be no bigger than a couple of bytes 我可以打开RTTI,但是空间成本看起来相当大,特别是当类型信息可以实现为指针并且对象大小可能不大于几个字节时

and

I want dense, unique type ID's that are available at compile time, so the compiler can perform well, like converting a switch on the type IDs into a constant time jump table, or at least a binary search tree 我想要在编译时可用的密集,唯一类型ID,因此编译器可以很好地执行,比如将类型ID上的开关转换为常量时间跳转表,或者至少是二进制搜索树

If more than one ID allocator is used (in a case of several libraries and developers) and TU must be interfaced on IDed types then the only way is to use GUIDs which values are rarefied and non sequential. 如果使用多个ID分配器(在多个库和开发人员的情况下)并且TU必须在IDed类型上接口,那么唯一的方法是使用GUID,其值是稀疏的和非顺序的。 But also GUID occupies 16 bytes and requires RTTI as well as type reflection. 但GUID也占用16个字节,需要RTTI和类型反射。 Otherwise in an attempt to build two libraries (which absolutely have different type <=> id maps) interfaced on ID of types into one module either a linker generates an error (thanks to @MooingDuck for remarks) or developers will interfere with their different assignments of IDs to types obtained either manually (using v2's DEF_TYPE_ID macro) or an ID generator (calling a complier by AUTO_TYPE_ID macro). 否则,在尝试构建两个库(绝对具有不同type <=> id map)的类型ID接口到一个模块中时,链接器会生成错误(感谢@MooingDuck备注)或者开发人员会干扰他们的不同分配对于手动获取的类型的ID(使用v2的DEF_TYPE_ID宏)或ID生成器(通过AUTO_TYPE_ID宏调用编译器)。

So there are cases which always are to be reduced to the first in order to use int ID of types: 所以有些情况总是要减少到第一个才能使用类型的int ID:

  1. there is the only ID allocator and the only type <=> ID map for all TU; 所有TU都有唯一的ID分配器和唯一的type <=> ID映射type <=> ID ;
  2. interface of TU is not depended on "system of classes that are small objects" with type <=> ID links, hence the first case for each TU; TU的接口不依赖于具有type <=> ID链接的“小对象类系统”,因此每个TU的第一种情况;
  3. a negotiation between developers on type <=> ID maps, produced by different ID allocators, is to be accomplished to get the only type <=> ID map. 由不同的ID分配器生成的type <=> ID映射的开发人员之间的协商将被完成以获得唯一的type <=> ID映射。

There is another approach but which does not meet "generate dense IDs" requirement. 还有另一种方法但不符合“生成密集ID”的要求。 The approach allows to get partially automatically generated a structured ID ie ID such as enum {FileID, LocalID}; typedef get_id<arg1_t>::res tmpl_arg_1_ID; ... 该方法允许部分自动生成结构化ID,即ID,例如enum {FileID, LocalID}; typedef get_id<arg1_t>::res tmpl_arg_1_ID; ... enum {FileID, LocalID}; typedef get_id<arg1_t>::res tmpl_arg_1_ID; ... enum {FileID, LocalID}; typedef get_id<arg1_t>::res tmpl_arg_1_ID; ... . enum {FileID, LocalID}; typedef get_id<arg1_t>::res tmpl_arg_1_ID; ... In this case FileID must be assigned manually to every file where a type <=> ID link is defined. 在这种情况下,必须手动将FileID分配给定义了type <=> ID链接的每个文件。 LocalID is generated by calling a complier with __LINE__ macro. 通过使用__LINE__宏调用编译器生成LocalID LocalID of a template is automatically assigned in a manner described below. 模板的LocalID以下面描述的方式自动分配。 IDs of template arguments such as tmpl_arg_1_ID is obtained automatically using get_id template. 的模板参数例如ID tmpl_arg_1_ID使用自动获得get_id模板。 The main advantage of such structured IDs is that they are static and constant for every library and TU due to a constant content of included files (but versioning becomes a problem). 这种结构化ID的主要优点是它们对于每个库和TU都是静态的和常量的,因为包含文件的内容不变(但版本化成为一个问题)。 In order to apply such a structured ID a multiple nested switch statements may be used beginning with FileID, then LocalID and so on. 为了应用这样的结构化ID,可以使用从FileID开始,然后使用LocalID等多个嵌套的switch语句。

Features and differences from v1 v1的功能和不同之处

  • Every template instance takes unique ID automatically and does not require any alias or forward declaration due to ID is allocated and defined as inner enum constant. 每个模板实例自动获取唯一ID,并且不需要任何别名或前向声明,因为ID已分配并定义为内部枚举常量。
  • A template takes own unique ID defined as enum constant inside a special "null" instance of a template such as T<null_t, null_t ...> named _BaseT where null_t type is given for all typename arguments. 模板在模板的特殊“null”实例中采用自定义为枚举常量的唯一ID,例如名为_BaseT的T<null_t, null_t ...> ,其中为所有typename参数指定了null_t类型。
  • Only IDs of template insnances are rarefied: they are values of hash function calculated from ID of template parameters and template ID accessed as _BaseT::ID . 仅稀疏模板insnances的ID:它们是根据模板参数的ID计算的散列函数的值和作为_BaseT::ID访问的模板_BaseT::ID Hash function is the same as defined in xhash header in MS VS 2005. 散列函数与MS VS 2005中的xhash标头中定义的相同。
  • Now the type <=> id map uses ID=0 returned in absence of type <=> ID link. 现在,在没有type <=> ID链接的情况下, type <=> id映射使用ID=0返回。
  • By default a template instance does not have associated type <=> ID link in the map. 默认情况下,模板实例在地图中没有关联的type <=> ID链接。 This is why get_id template is used to access ID by type. 这就是使用get_id模板按类型访问ID的原因。
  • __COUNTER__ macro is used to reduce and simplify code: PREV_TYPE macro does not need anymore. __COUNTER__宏用于减少和简化代码: PREV_TYPE宏不再需要了。
  • There is no need to use the trick with template data_t from v1 due to forward declarations and internal template instance's ID. 由于前向声明和内部模板实例的ID,不需要使用来自v1的模板data_t的技巧。
  • Now type <=> id link with automatically generated ID is to be defined with AUTO_TYPE_ID(type_name) macro. 现在,使用AUTO_TYPE_ID(type_name)宏定义带有自动生成的ID的type <=> id链接。
  • Also type <=> id link may be defined with an ID allocated by another allocator (ie manually) using DEF_TYPE_ID(type_name, id) macro. 此外, type <=> id链接可以使用由另一个分配器(即手动)使用DEF_TYPE_ID(type_name, id)宏分配的ID来定义。 But if you use both macros a resolution of collision ID assignments become a trouble. 但是如果你使用两个宏,碰撞ID分配的分辨率就会变成麻烦。

The core -- type_id_map_t_cnt header 核心 - type_id_map_t_cnt标头

#ifndef __TYPE_ID_MAP_T_CNT__
#define __TYPE_ID_MAP_T_CNT__

//use it if there is __COUNTER__ macro and rarefied random ID is allowable
namespace ns_type_ids {
    typedef unsigned int uint;
    typedef unsigned long long ulint;
    typedef unsigned short ushort;

    //`type <=> id` link
    struct null_t { enum {ID=__COUNTER__}; };
    template<class T, int ID_v>
    struct ID_T_pair {
        enum {ID=ID_v};
        typedef T _Type;
        inline static const _TCHAR * name() { return _T("unassigned"); }
    };

    //accessors for `type <=> id` link
    template<class T> struct ID_by_T: ID_T_pair<T, null_t::ID> {};
    template<int ID_v> struct T_by_ID: ID_T_pair<null_t, ID_v> {};

    //predefined `type <=> id` link for null_t and ID=0
    template<> struct ID_by_T<null_t>: ID_T_pair<null_t, null_t::ID> {
        inline static const _TCHAR * name() { return _T("null_t"); }
    };
    template<> struct T_by_ID<ID_by_T<null_t>::ID>: ID_by_T<null_t> {};

    //function for generating IDs inside an instance of class template
    //2166136261U and 16777619U constants are from xhash STL implementation
    template<ushort v, uint a=2166136261U>
    struct hash {
        enum : uint {res=(uint)((ulint)16777619U * (ulint)a ^ (ulint)v)};
    };

    //ternary operator ?:
    template <bool, class Yes, class No>struct IIF { typedef null_t res; };
    template <class Yes, class No> struct IIF<true, Yes, No> { typedef Yes res; };
    template <class Yes, class No> struct IIF<false, Yes, No> { typedef No res; };

    //accessor to ID of type for both `type <=> ID` link and ID of a template instance
    template <class T>
    struct get_id {
        typedef typename IIF<
        //by default there is no `type <=> ID` link for a teamplate instance
        //instead ID is allocated and defined inside.
            ID_by_T<T>::ID == null_t::ID
        ,   T
        ,   ID_by_T<T>
        >::res _IDed;
        // this `::ID` interface coincedences for
        // ID_T_pair, a template instance T and null_t
        enum : uint {res=_IDed::ID};
    };
};

// DEF_TYPE_ID macro to define `type <=> id` link
#define DEF_TYPE_ID(type_name, type_id) \
namespace ns_type_ids { \
    template<> struct ID_by_T<type_name>: ID_T_pair<type_name, type_id> { \
        inline static const _TCHAR * name() { return _T(#type_name); } \
    }; \
    template<> struct T_by_ID<ID_by_T<type_name>::ID>: ID_by_T<type_name> {}; \
};

// AUTO_TYPE_ID macro to allocate new ID and define `type <=> id` link
#define AUTO_TYPE_ID(type_name) DEF_TYPE_ID(type_name, __COUNTER__)

#endif /* __TYPE_ID_MAP_T_CNT__ */

The use of type <=> id map example in templated_cls_id.cpp 在templated_cls_id.cpp中使用type <=> id map示例

#include <tchar.h>
#include <iostream>

#ifdef _UNICODE
#define _tcout wcout
#else
#define _tcout cout
#endif

#include "type_id_map_t_cnt"

//Now `type <=> id` link definition became very short
AUTO_TYPE_ID(int)
AUTO_TYPE_ID(double)
AUTO_TYPE_ID(float)

//Use forward declaration of a template and a specialization with null_t
//to define special base type with ID of the template
template<class T> struct tmpl_id;
template<> struct tmpl_id<ns_type_ids::null_t>;

//Now "null template" is known for the compiler
AUTO_TYPE_ID(tmpl_id<ns_type_ids::null_t>)

//The "null template" specialization
//Realy _BaseT type alias it the "null template" specialization
template<> struct tmpl_id<ns_type_ids::null_t> {
    //returns the same base ID for every class instance
    typedef tmpl_id<ns_type_ids::null_t> _BaseT;
    //generating ID and defining its container
    typedef ns_type_ids::hash<ns_type_ids::ID_by_T<_BaseT>::ID> _Hash;
    //This is the ID of template tmpl_id
    enum {ID=_Hash::res};
};

//Now the target template can be defined.
//tmpl_id<ns_type_ids::null_t> is the base type for all template instances.
//_BaseT is inherited from the base type.
template<class T>
struct tmpl_id: tmpl_id<ns_type_ids::null_t> {
    //unique rarefied calculated ID for every class instance
    typedef ns_type_ids::hash<        
        ns_type_ids::get_id<T>::res
    ,   _BaseT::ID // it is already hash value
                   // and the second calling hash with it is not needed
    > _Hash;
    enum {ID=_Hash::res};
};

int _tmain(int argc, _TCHAR* argv[]) {
    using namespace std;
    using namespace ns_type_ids;

    typedef int int_alias; //for testing behaviour on alias of int
    //Now get_id is used instead of direct access with ID_by_T
#define out_id(type_name) \
    _T("ID_by_T<") _T(#type_name) _T(">::ID: ") << get_id<type_name>::res
#define out_name(type_id) \
    _T("T_by_ID<") _T(#type_id) _T(">: ") << T_by_ID<type_id>::name()

    _tcout
        << _T("ID_by_T -- getting ID of type") << endl
        << out_id(null_t) << endl
        << out_id(int) << endl
        <<_T("ID_of_T<type_alias> works as expected") << endl
        << out_id(int_alias) << endl
        << out_id(double) << endl
        << out_id(float) << endl
        << out_id(tmpl_id<null_t>) << endl
        << out_id(tmpl_id<int>) << endl
        << out_id(tmpl_id<double>) << endl
        << out_id(tmpl_id<float>) << endl
        /* Next commented line generates an error to indicate
           absence of ID for the char type */
        //<< out_id(tmpl_id<char>) << endl 
        << endl
        << _T("T_by_ID -- getting type or its name by ID") << endl
        << out_name(-1) << endl
        << out_name(0) << endl
        << out_name(1) << endl
        << out_name(2) << endl
        << out_name(3) << endl
        << out_name(4) << endl
        << out_name(5) << endl
    ;
    return 0;
#undef out_id
#undef out_name
}

Output: 输出:

ID_by_T -- getting ID of type
ID_by_T<null_t>::ID: 0
ID_by_T<int>::ID: 1
ID_of_T<type_alias> works as expected
ID_by_T<int_alias>::ID: 1
ID_by_T<double>::ID: 2
ID_by_T<float>::ID: 3
ID_by_T<tmpl_id<null_t>>::ID: 4
ID_by_T<tmpl_id<int>>::ID: 225874304
ID_by_T<tmpl_id<double>>::ID: 225874307
ID_by_T<tmpl_id<float>>::ID: 225874306

T_by_ID -- getting type or its name by ID
T_by_ID<-1>: unassigned
T_by_ID<0>: null_t
T_by_ID<1>: int
T_by_ID<2>: double
T_by_ID<3>: float
T_by_ID<4>: tmpl_id<ns_type_ids::null_t>
T_by_ID<5>: unassigned

If you know how to calculate sequential IDs for template instances please let me know to rewrite ns_type_ids::hash :-) 如果您知道如何计算模板实例的顺序ID,请告诉我重写ns_type_ids::hash :-)

I think what you're asking for is basically impossible in C++. 我认为你要求的东西在C ++中基本上是不可能的。 The counter cannot be known at compile time, because individual compilation units do not know about each other, so you're pretty much on a hiding to nothing there. 计数器在编译时是不可知的,因为个别编译单元彼此不了解,所以你几乎都隐藏在那里。

Instead I'm using the following approach, which still isn't at "compile-time", but at least doesn't incur a function-call overhead when you query the type (assuming the compiler respects the inline), and is threadsafe. 相反,我正在使用以下方法,它仍然不在“编译时”,但至少在查询类型时(假设编译器遵循内联)不会产生函数调用开销,并且是线程安全的。

RuntimeID.h RuntimeID.h

//-----------------------------------------------
class CNextRuntimeID 
{
protected:
  static long m_NextRuntimeID;
};

//-----------------------------------------------

template<class T>
class CIntegerRuntimeTypeID: public CNextRuntimeID
{
  static const long m_RuntimeID;
public:
  inline static long GetRuntimeID()
  {
    return m_RuntimeID;
  }
};

template<class T> 
const long CIntegerRuntimeTypeID<T>::m_RuntimeID = CNextRuntimeID::m_NextRuntimeID++;

RuntimeID.cpp RuntimeID.cpp

long CNextRuntimeID::m_NextRuntimeID = 0;

I've thought quite a bit about this implementation, and I believe it's safe. 我对这个实现有很多想法,我相信它是安全的。 A potential issue is that m_NextRuntimeID could in theory be initialised to zero after one of the m_RuntimeIDs are, which would obviously result in duplicate values. 一个潜在的问题是m_NextRuntimeID理论上可以在其中一个m_RuntimeID之后初始化为零,这显然会导致重复值。 However, under VisualStudio at least, the initialisation to zero does not generate code, whereas the counter-based initialisations do. 但是,至少在VisualStudio下,初始化为零不会生成代码,而基于计数器的初始化则会生成代码。

Unfortunately, if you really care about code space, you may not like the fact that each of the increments are placed inside a function, and those functions take up space. 不幸的是,如果你真的关心代码空间,你可能不喜欢这样一个事实:每个增量都放在一个函数中,而这些函数占用了空间。 Less space than the usual 'static local variable' non-threadsafe approach, but space nevertheless. 比通常的“静态局部变量”非线程安全方法更少的空间,但仍然是空间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在编译时生成唯一的数字 - Generate unique numbers at compile time 我如何编写一个可变参数函数,在编译时将 ID 转换为变量并用这些函数调用另一个函数? - How can I write a variadic function that in compile time translates IDs to variables and calls another function with those instead? 如何在编译时验证模板 function 参数的类型 - How can I validate the type of a template function argument at compile time 在编译时如何未知对象类型? - How can an object type be unknown at compile time? 如何在C ++中生成中断处理程序的编译时数组? - How can I generate a compile-time array of interrupt handlers in C++? 在编译时生成唯一类型标识符 - Generating unique type identifier at compile time 如何在编译时关闭功能? - How can I gate features at compile time? 如何将 void 模板类型用于编译时条件代码和 static_assert? - How can I use void template type for compile-time conditional code and static_assert? 如何在编译时推断嵌套 std::vector 的内部类型? - How can I deduce the inner type of a nested std::vector at compile time? 如何在此回调中将void指针替换为具有编译时类型检查的对象? - How can I replace the void pointer with something that has compile time type checks in this callback?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM