今天我开发了另一种解决方案,可以自动为每个模板实例分配ID,而无需为每个“IDed”模板实例定义别名。该解决方案名为v2,基于先前称为v1的解决方案。需要将ID分配给基本类型的v1功能是自动分配唯一ID给每个模板实例所必需的。
更新:关于选择唯一ID分配器的重要说明
此处解决的问题与任务和两个答案都有关系。由于要求,任务的方法意味着仅使用一个ID分配器:
我可以打开RTTI,但空间成本似乎相当大,特别是当类型信息可以实现为指针且对象大小可能不超过几个字节时。
和
我希望稠密、唯一的类型ID在编译时可用,这样编译器就可以表现良好,例如将对类型ID的switch转换为常量时间跳转表,或至少是二进制搜索树。
如果使用多个ID分配器(在几个库和开发人员的情况下),并且TU必须在ID类型上进行接口,则唯一的方法是使用GUID,其值稀疏且非连续。但是,GUID占用16个字节,并且需要RTTI以及类型反射。否则,在尝试将两个库(它们绝对具有不同的type <=> id映射)构建为一个模块时,链接器将生成错误(感谢@MooingDuck的备注),或者开发人员将干扰它们不同的ID分配到类型中,这些类型是手动获取的(使用v2的DEF_TYPE_ID宏)或通过调用编译器的ID生成器(使用AUTO_TYPE_ID宏)获得的。
因此,总是需要将以下情况缩小为第一种情况以使用类型的int ID:
1.只有一个ID分配器和所有TU的唯一类型<=>ID映射;
2.TU的接口不取决于“作为小对象的类系统”与type <=> ID链接,因此每个TU的第一种情况;
3.必须在不同ID分配器生成的type <=> ID映射之间进行开发人员协商,以获得唯一的type <=> ID映射。
还有另一种方法,但它不符合“生成稠密ID”的要求。该方法允许部分自动生成结构化ID,例如enum {FileID, LocalID}; typedef get_id<arg1_t>::res tmpl_arg_1_ID; ...
这样的ID。在这种情况下,必须手动为每个定义了type <=> ID
链接的文件分配FileID
。通过调用带有__LINE__
宏的编译器生成LocalID
。模板的LocalID
会自动分配,方法如下描述。使用get_id
模板可以自动获得诸如tmpl_arg_1_ID
之类的模板参数ID。这些结构化ID的主要优点是它们对于每个库和TU都是静态和恒定的,因为包含的文件内容是恒定的(但版本控制是一个问题)。为了应用这样的结构化ID,可以使用多个嵌套的switch语句,从FileID开始,然后是LocalID等。
v1版本的特点和差异
- 每个模板实例都会自动分配唯一的ID,无需别名或前向声明,因为ID是作为内部枚举常量分配和定义的。
- 模板通过特殊的"null"实例中定义的枚举常量作为自己独特的ID,例如
T<null_t, null_t ...>
命名为_BaseT,其中所有typename参数都给出了null_t
类型。
- 仅模板实例的ID是稀疏的:它们是从模板参数ID和访问的模板ID计算的哈希函数的值,如
_BaseT::ID
。哈希函数与MS VS 2005中定义的xhash头文件中定义的相同。
- 现在
type <=> id
映射使用缺少type <=> ID
链接时返回的ID=0
。
- 默认情况下,模板实例的地图中没有关联的
type <=> ID
链接。这就是为什么使用get_id
模板来通过类型访问ID的原因。
- 使用
__COUNTER__
宏可以减少和简化代码:PREV_TYPE
宏不再需要。
- 由于前向声明和内部模板实例的ID,不再需要使用来自v1的模板
data_t
技巧。
- 现在可以使用
AUTO_TYPE_ID(type_name)
宏定义具有自动生成ID的type <=> id
链接。
- 还可以使用
DEF_TYPE_ID(type_name, id)
宏使用由另一个分配器(即手动)分配的ID定义type <=> id
链接。但是,如果同时使用这两个宏,则冲突ID分配的解析会成为问题。
核心-- type_id_map_t_cnt 头文件
#ifndef __TYPE_ID_MAP_T_CNT__
#define __TYPE_ID_MAP_T_CNT__
namespace ns_type_ids {
typedef unsigned int uint;
typedef unsigned long long ulint;
typedef unsigned short ushort;
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"); }
};
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> {};
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> {};
template<ushort v, uint a=2166136261U>
struct hash {
enum : uint {res=(uint)((ulint)16777619U * (ulint)a ^ (ulint)v)};
};
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; };
template <class T>
struct get_id {
typedef typename IIF<
ID_by_T<T>::ID == null_t::ID
, T
, ID_by_T<T>
>::res _IDed;
enum : uint {res=_IDed::ID};
};
};
#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> {}; \
};
#define AUTO_TYPE_ID(type_name) DEF_TYPE_ID(type_name, __COUNTER__)
#endif
在templated_cls_id.cpp中使用
type <=> id
映射示例
#include <tchar.h>
#include <iostream>
#ifdef _UNICODE
#define _tcout wcout
#else
#define _tcout cout
#endif
#include "type_id_map_t_cnt"
AUTO_TYPE_ID(int)
AUTO_TYPE_ID(double)
AUTO_TYPE_ID(float)
template<class T> struct tmpl_id;
template<> struct tmpl_id<ns_type_ids::null_t>;
AUTO_TYPE_ID(tmpl_id<ns_type_ids::null_t>)
template<> struct tmpl_id<ns_type_ids::null_t> {
typedef tmpl_id<ns_type_ids::null_t> _BaseT;
typedef ns_type_ids::hash<ns_type_ids::ID_by_T<_BaseT>::ID> _Hash;
enum {ID=_Hash::res};
};
template<class T>
struct tmpl_id: tmpl_id<ns_type_ids::null_t> {
typedef ns_type_ids::hash<
ns_type_ids::get_id<T>::res
, _BaseT::ID
> _Hash;
enum {ID=_Hash::res};
};
int _tmain(int argc, _TCHAR* argv[]) {
using namespace std;
using namespace ns_type_ids;
typedef int int_alias;
#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
<< 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
如果您知道如何计算模板实例的顺序ID,请告诉我以便重写
ns_type_ids::hash
:-)
Square<int>
和Square<double>
会有相同的ID吗? - Mat