C++ 如何在编译时生成密集的唯一类型ID?
我试图创建一个小对象类系统,基类有一个成员,该成员是标识类的唯一标识符:C++ 如何在编译时生成密集的唯一类型ID?,c++,templates,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; };
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;
};
类形状
{
公众:
无符号字符id;
};
模板
类三角形:公共形状
{
T三角数据;
};
模板
阶级广场:公共形态
{
T平方分布数据;
};
模板
类ShapeBox:公共形状
{
T形盒_数据;
形状*儿童形状;
};
使用类标识符,我遍历Shape*向量并打开基类中可见的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
{
静态尺寸计数器;
公众:
模板
静态大小\u t值()
{
静态大小_t id=计数器++;
返回id;
}
};
大小\u t类型ID::计数器=1;
理想情况下,我需要在编译时可用的密集、唯一的类型ID,这样编译器就可以很好地执行,比如将类型ID上的开关转换为一个常量时间跳转表,或者至少转换为一个二进制搜索树,而不是一个线性时间if/else链,用于可能是一个很长的类型ID列表
我可以在编译时使用样板文件手动分配每个类型ID,也可以使用来自类似类型ID类的对象/函数指针。Boiler plate保证是稠密的(因为我们手动分配),并且在编译时已知,但对于模板类型来说它是不可维护的。无论何时向形状添加模板类型,都必须手动添加新类型。单调静态计数器是可维护且密集的,但在编译时未知,因此不可能进行编译时优化,线程安全可能是一个问题。函数指针ID在编译时是已知的,并且是可维护的,但是它不是密集的,并且不适合像char这样的小ID类型
是否有任何方法可以生成编译器在编译时可见、密集且自动分配的类型ID,可以使用模板元编程计数器或C++11或C++14中的某些预处理器魔法?或者,这是不可能的,直到C++有编译时间反射?< /P>
有没有办法生成编译器可见的类型ID
在编译时,密集和自动分配,可能使用
模板元编程计数器
我已经开发了一个代码,它几乎没有限制。
两个模板专门化ID\u by\u T
和T\u by\u ID
在编译时定义type ID
链接。类型的ID是枚举常量。如果未定义type ID
链接,则ID\u by\u T::ID
是-1
,T\u by\u ID::type
是null\u T
预定义类型<代码>定义类型ID(类型名称)宏在定义类型ID
链接时生成新IDint\u triangle
和char\u triangle
显示如何获取具有正确类型ID的typedef,而内部typedef\u MyID\u T
显示如何获取类型ID。代码采用MS vs 2005 C++编写。
核心--类型\u id\u映射头文件:
#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
#ifndef(类型)ID(映射)__
#定义类型ID映射__
名称空间ns\U类型\U ID{
结构null_t{};
模板
结构ID\u T\u对{
枚举{ID=ID_v};
类型定义T_类型;
内联静态常量_TCHAR*name(){return _T(“未知”);}
};
模板结构ID_by_T:ID_T_对{};
模板结构T_by_ID:ID_T_对{};
模板结构ID\u by\u T:ID\u T\u对{
内联静态常量_TCHAR*name(){return _T(“null_T”);}
};
模板结构T_by_ID:ID_by_T{};
};
#定义上一个类型null
#定义定义类型ID(类型名称)\
名称空间ns_类型_ID{\
模板结构ID_by_T:ID_T_对{\
内联静态常量_TCHAR*name(){return _T(#type _name);}\
}; \
模板结构T_by_ID:ID_by_T{}\
};
#恩迪夫
以及在模板化的\u cls\u id.cpp中使用类型\u id\u映射示例:
#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
}
#包括
#包括
#ifdef_UNICODE
#定义输出wcout
#否则
#定义输出值
#恩迪夫
#包括“类型\标识\地图”
//目标类型
结构形状{};
模板
结构数据:形状{
T_数据;
};
模板
结构三角形:数据类型、ns类型、ID::ID{
typedef data_t_MyID_t;
};
//开始类型id映射
定义类型标识(int)
#未定义上一类型
#定义上一个类型int
DEF_类型_ID(双精度)
#未定义上一类型
#定义PREV_类型double
DEF_类型_ID(浮动)
#未定义上一类型
#定义上一个浮点数类型
定义类型标识(数据)
#未定义上一类型
#定义上一类型数据
定义类型标识(数据)
#未定义上一类型
#定义上一类型数据
//端类型id映射
//现在可以定义目标类了
typedef三角形int_三角形;
typedef三角形char_三角形;
int _tmain(int argc,_TCHAR*argv[]{
使用名称空间std;
使用名称空间ns_type_id;
#定义输出标识(键入名称)\
_T(“ID_by_T::ID:”)今天,我开发了另一个解决方案,可以自动为每个模板实例分配ID,而无需为每个“IDed”定义别名模板实例。名为v2的解决方案基于先前的v1。v1为基本类型分配ID的功能需要自动为每个模板实例分配唯一ID
UPD:关于选择唯一ID分配器的重要注意事项
此处解决的问题与任务相关,但两者都有答案。由于需求,任务的方法意味着唯一的ID分配器:
我可以打开RTTI,但空间成本似乎很高
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>
#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__ */
#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
}
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
//-----------------------------------------------
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++;
long CNextRuntimeID::m_NextRuntimeID = 0;