如何在编译时生成密集的唯一类型ID? [英] How can I generate dense unique type IDs at compile time?
问题描述
<$ p 我尝试创建一个小对象的类系统,基类有一个唯一的标识符标识该类的成员: $ p>
class Shape
{
public:
unsigned char id;
};
template< typename T>
类三角形: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 *向量,并打开基类,然后为不同的行为(对于三角形,正方形或ShapeBox和子类形状分别保存在示例类层次结构中)的静态强制转换
我可以打开RTTI ,但是空间成本似乎相当大,特别是当类型信息可以实现为指针,并且对象大小可能不大于几个字节时。可能有数百万的小对象,我真的只需要静态cast。
目前,我可以使用从静态单调递增计数器分配值的静态数据类型标识符:
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上的开关转换为常量时间跳转表,或者至少是二进制搜索树而不是线性时间if / else链可能是ID类型的长列表...
我可以在编译时使用样板来手动分配每个类型ID,或者我可以使用来自类似ID类的对象/函数指针。锅炉板保证是密集的(因为我们手动分配),并在编译时知道,但它对于模板类型是不可维护的。每当向模型添加模板类型时,都必须手动添加新类型。单调静态计数器是可维护和密集的,但在编译时是未知的,因此编译时优化是不可能的,并且线程安全性可能是一个问题。函数指针ID在编译时是已知的,并且是可维护的,但不是密集的,并且不适合像char这样的小id类型。
生成编译器在编译时可见的类型ID,密集和自动分配,可能使用模板元编程计数器或C ++ 11或C ++ 14中的一些预处理器魔术?或者这是不可能的,直到C ++有编译时间反射?
类型编译器可见的ID
在编译时,密集并自动分配,可能使用
模板元编程计数器
我开发了一个代码,这样做有很少的限制。
两个模板特化 ID_by_T
和 T_by_ID
define type< =& ID
链接。类型的ID是枚举常量。如果 type< => ID 链接未定义
是 ID_by_T< type> :: ID
是 -1
code> T_by_ID< undefinedID> :: type null_t
预定义类型。 DEF_TYPE_ID(type_name)
宏在定义时生成新ID类型< => ID
链接。 int_triangle
和 char_triangle
显示如何获取typedef正确的类型ID和内部typedef _MyID_T
显示如何获取类型的ID。代码是用MS VS 2005 C ++编译的。
核心 - type_id_map 头文件:
#ifndef __TYPE_ID_MAP__
#define __TYPE_ID_MAP__
命名空间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> {};
模板<> struct ID_by_T< null_t> ;:ID_T_pair< null_t,-1> {
inline static const _TCHAR * name(){return _T(null_t); }
};
模板<> 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_map templated_cls_id.cpp 中的示例:
#include< tchar.h>
#include< iostream>
#ifdef _UNICODE
#define _tcout wcout
#else
#define _tcout cout
#endif
#includetype_id_map
//目标类型
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
//现在可以定义目标类别
typedef triangle< int> int_triangle;
typedef triangle< char> char_triangle;
int _tmain(int argc,_TCHAR * argv []){
using namespace std;
使用命名空间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>
要求和限制:
-
类型< => ID
地图是全局的,只在编译时有效。 -
类型< =& ID
链接必须使用DEF_TYPE_ID
和PREV_TYPE
宏在全局命名空间级别定义。 li>
- IDed类型必须在定义
之前声明类型< => ID
链接。 - 为了在类中使用自身ID,请使用
ID_by_T< self_type>
基类其中self_type是一个自己类型的类。 - 为了在模板类中获取自己的ID,请使用
ID_by_T
作为基类其中base_data_type是用于 type< =>的特殊基本类型的模板类。 ID
链接已定义。例如,请参见int_triangle
和char_triangle
。
功能
- ID是外部的,并且在编译时自动按顺序自动分配,以
; => ID
链接定义。 - 对编译器的最低要求:只有
ISO / IEC 14882:2003 SE的标准功能
。 - 宏
__ COUNTER __
,__ LINE __
,BOOST_PP_COUNTER
或基于sizeof
不用于分配ID:没有与它们相关的副作用。 -
type< => id
映射基于编译时已知的外部标识:
- ID可以分配给每个类型,甚至基本类型。 li>
-
ID_T_pair
模板描述type< => id
链接。ID_by_T
/T_by_ID
模板是ID_T_pair
模板的直接后代。 - 由于
ID_by_T
模板,无需在类型中定义ID(基本类型不可能实现) li>
- ID类型是通过
ID_by_T< type> :: ID
枚举常量访问的。 - 通过ID访问
T_by_ID< ID> :: _ Type
inner typedef。 - 可选:类型的名称使用
- $
未使用
- ID可以分配给每个类型,甚至基本类型。 li>
type< =>
null_t
和 ID = -1
在没有类型< => ID
链接。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)
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. 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...
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. 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.
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? Or is this not possible until C++ has compile time reflection?
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
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. Type's ID is a enum constant. 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. DEF_TYPE_ID(type_name)
macro generates new ID when defines type <=> ID
link. 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. The code was compiled with MS VS 2005 C++.
The core -- type_id_map header file:
#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:
#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:
Type <=> ID
map is global and works only at compile time.Type <=> ID
link must be defined at global namespace level usingDEF_TYPE_ID
andPREV_TYPE
macro.- "IDed" type must be declared prior to defintion of
Type <=> ID
link. - 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. But why (see below)? - 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 wichtype <=> ID
link is already defined. Seeint_triangle
andchar_triangle
for example. Also there are other tricks to get defined ID inside a template instance.
Features
- IDs are external and allocated at compile time automatically sequentially beginning with 0 in the compilation order of
type <=> ID
link definitions. This is inevitability due to the requirement of the question. - Minimal requirements to a compiler: only standard features of
ISO/IEC 14882:2003 SE
. - Macros
__COUNTER__
,__LINE__
,BOOST_PP_COUNTER
or based onsizeof
are not used to allocate ID: there are no side effects associated with them. - The
type <=> id
map is based on external IDs known at compile time:- An ID can be assigned to every type, even to a fundamental type.
ID_T_pair
template describestype <=> id
link.ID_by_T
/T_by_ID
templates are direct descendants ofID_T_pair
template.- 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 type is accessed with
ID_by_T<type>::ID
enum constant. - Type by ID is accessed with
T_by_ID<ID>::_Type
inner typedef. - Optional: type's name is accessed with
name()
method ofID_T_pair
. - The map does not occupy any memory byte if method
name()
ofID_T_pair
is not used.
- The map is distributed:
type <=> id
link can be defined in place but at global namespace level. - In order to access the map in a few TU a special header may be used.
- Definition of a composed derived type does not require any additional info for the map.
- The map uses special null type
null_t
andID=-1
returned in absence oftype <=> ID
link.
这篇关于如何在编译时生成密集的唯一类型ID?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!