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

dez*_*kin 10 c++ templates

我正在尝试创建一个小型对象的类系统,并且基类的成员是标识该类的唯一标识符:

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;
};
Run Code Online (Sandbox Code Playgroud)

使用类标识符,我通过Shape*的向量并切换基类中可见的id,然后针对不同的行为进行静态转换(分别为示例类层次结构中的Triangle,Square或ShapeBox和子形状) )

我可以打开RTTI,但是空间成本似乎相当大,特别是当类型信息可以实现为指针并且对象大小可能不大于几个字节时.可能有数百万个小物件,我真的只需要静态铸造.

目前,我可以使用从静态单调递增计数器分配值的静态来创建类型标识符:

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;
Run Code Online (Sandbox Code Playgroud)

理想情况下,我需要在编译时可用的密集,唯一类型ID,因此编译器可以很好地执行,例如将类型ID上的开关转换为常量时间跳转表,或者至少是二进制搜索树而不是线性时间/ else链可能是一长串的类型ID列表...

我可以在编译时使用样板来手动分配每个类型ID,或者我可以使用来自类似类型ID类的对象/函数指针.锅炉板保证密集(因为我们手动分配)并且在编译时已知,但它对于模板类型是不可维护的.每当向模板添加模板类型时,都必须手动添加新类型.单调静态计数器是可维护和密集的,但在编译时是未知的,因此编译时优化是不可能的,并且线程安全可能是一个问题.函数指针ID在编译时是已知的并且是可维护的,但是不是密集的,并且不适合像char这样的小id类型.

有没有办法生成编译时编译器可见,密集和自动分配的类型ID,可能使用模板元编程计数器或C++ 11或C++ 14中的一些预处理器魔术?或者这是不可能的,直到C++有编译时反射?

Ale*_* F. 4

有没有办法生成编译器在编译时可见的、密集的、自动分配的类型ID,也许使用模板元编程计数器

我开发了一个代码,它几乎没有限制。两个模板专业化ID_by_T并在编译时T_by_ID定义链接。type <=> ID类型的 ID 是一个枚举常量。如果type <=> ID链接未定义,则为ID_by_T<type>::ID预定义类型。宏在定义链接时生成新的 ID 。并展示如何获取具有正确类型 ID 的 typedef,内部 typedef展示如何获取类型 ID。该代码是用 MS VS 2005 C++ 编译的。-1T_by_ID<undefinedID>::typenull_tDEF_TYPE_ID(type_name)type <=> IDint_trianglechar_triangle_MyID_T

核心——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
Run Code Online (Sandbox Code Playgroud)

以及templated_cls_id.cpptype_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
}
Run Code Online (Sandbox Code Playgroud)

输出:

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>
Run Code Online (Sandbox Code Playgroud)

要求和限制:

  1. Type <=> IDmap 是全局的并且仅在编译时起作用。
  2. Type <=> IDDEF_TYPE_ID必须使用和宏在全局命名空间级别定义链接PREV_TYPE
  3. “IDed”类型必须在定义链接之前声明Type <=> ID
  4. 为了在类中获取 self ID,请使用ID_by_T<self_type>作为基类,其中 self_type 是类自己的类型。但为什么(见下文)?
  5. 为了在模板类中获取自身 ID,请使用ID_by_T<base_data_type>作为基类,其中 base_data_type 是type <=> ID已定义链接的模板类的特殊基类型。参见int_trianglechar_triangle例如。还有其他技巧可以获取模板实例中定义的 ID。

特征

  • ID 是外部的,并在编译时自动按链接定义的编译顺序从 0 开始按顺序分配type <=> ID。这是由于问题的要求而产生的必然性。
  • 对编译器的最低要求:只有ISO/IEC 14882:2003 SE.
  • __COUNTER____LINE__BOOST_PP_COUNTERbased onsizeof不用于分配 ID:没有与它们相关的副作用。
  • type <=> id映射基于编译时已知的外部 ID:
    • 可以将 ID 分配给每种类型,甚至是基本类型。
    • ID_T_pair模板描述type <=> id链接。ID_by_T/ T_by_IDtemplates 是 template 的直接后代ID_T_pair
    • 由于ID_by_T模板,不需要在类型内部定义 ID(这对于基本类型来说是不可能的)。
    • ID_by_T<type>::ID按类型划分的 ID 通过枚举常量进行访问。
    • 通过 ID 的类型是通过T_by_ID<ID>::_Type内部 typedef 访问的。
    • name()可选:类型的名称可以通过方法访问ID_T_pair
    • 如果不使用name()方法,则映射不占用任何内存字节。ID_T_pair
  • 映射是分布式的:type <=> id可以在全局名称空间级别就地定义链接。
  • 为了访问几个 TU 中的映射,可以使用特殊的标头。
  • 组合派生类型的定义不需要映射的任何附加信息。
  • 该地图使用特殊的空类型null_t并在没有链接ID=-1的情况下返回type <=> ID