gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 16:07:14 26 4
gpt4 key购买 nike

我正在尝试创建一个类系统,这些类是小对象,基类有一个成员,它是标识该类的唯一标识符:

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

使用类标识符,我遍历 Shape * 的 vector 并打开在基类中可见的 id,然后针对不同的行为进行静态转换(针对三角形、正方形或 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;

理想情况下,我想要在编译时可用的密集、唯一的类型 ID,这样编译器就可以很好地执行,例如将类型 ID 上的开关转换为恒定时间跳转表,或者至少是二叉搜索树而不是可能是一长串类型 ID 的线性时间 if/else 链...

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

有没有办法生成编译器在编译时可见的、密集的和自动分配的类型 ID,也许使用模板元编程计数器或 C++11 或 C++14 中的一些预处理器魔法?或者这在 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_by_TT_by_ID定义 type <=> ID编译时链接。类型的 ID 是一个枚举常量。如果type <=> ID链接未定义ID_by_T<type>::ID-1T_by_ID<undefinedID>::typenull_t预定义类型。 DEF_TYPE_ID(type_name)宏在定义 type <=> ID 时生成新 ID关联。 int_trianglechar_triangle显示如何使用正确的类型 ID 和内部 typedef 获取 typedef _MyID_T显示如何获取类型的 ID。代码使用 MS VS 2005 C++ 编译。

核心——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

以及 type_id_maptemplated_cls_id.cpp 中的使用示例:

#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
}

输出:

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>

要求和限制:

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

特点

  • ID 是外部的,并在编译时按顺序自动分配,在 type <=> ID 的编译顺序中从 0 开始链接定义。由于问题的需要,这是必然的。
  • 对编译器的最低要求:只有 ISO/IEC 14882:2003 SE 的标准功能.
  • __COUNTER__ , __LINE__ , BOOST_PP_COUNTER 或基于 sizeof不用于分配 ID:它们没有副作用。
  • type <=> id map 基于编译时已知的外部 ID:
    • ID 可以分配给每个类型,甚至是基本类型。
    • ID_T_pair模板描述 type <=> id关联。 ID_by_T/T_by_ID模板是 ID_T_pair 的直接后代模板。
    • 由于ID_by_T模板没有必要在类型内部定义 ID(基本类型不可能做到这一点)。
    • 按类型访问 ID 使用 ID_by_T<type>::ID枚举常量。
    • 使用 T_by_ID<ID>::_Type 访问 ID 类型内部类型定义。
    • 可选:使用 name() 访问类型的名称ID_T_pair的方法| .
    • 如果方法name(),映射不占用任何内存字节的 ID_T_pair未使用。
  • map 分发:type <=> id链接可以在全局命名空间级别就地定义。
  • 为了访问少数 TU 中的 map ,可以使用特殊的 header 。
  • 组合派生类型的定义不需要映射的任何附加信息。
  • map 使用特殊的空类型null_tID=-1在没有 type <=> ID 的情况下返回链接。

关于c++ - 如何在编译时生成密集的唯一类型 ID?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26570904/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com