我的世界java粘贴指令
TypeList固然精妙,但是就像链表相对数组有其不足一样,TypeList中的类型递归定义使得取得其中某个类型会变成比较麻烦的事,从而也把算法的复杂性提高了,或许这些都还能忍受,比较Loki::TypeList提供了足够的算法供我们使用,但是TypeList的一个致命弱点却在我编写通讯兵模式时候发现了。
4.1 TypeList的不足
当我的程序需要记录某个函数的参数类型时候我们用到TypeList很方便,
例如下面的程序:
template<typename class DoSomeThing { typedef typedef typedef public: void void void void };我们可以用不同类型特化DoSomeThing以后根据参数个数不同调用成员函数Do的重载体,这个技法在我后面的程序中将会出现很多,这就可以让我们在特化DoSomeThing之前不用知道具体会有几个参数或者会是什么类型,这就达到了泛化的目的。
但是问题出来了,如果我们需要Do的重载体接收的参数类型不是一个单数据类型,而是需要接收一个TypeList容器的时候,TypeList似乎就开始无能为力了,因为我们虽然可以写出类型这样的代码
typedefint,unsigned int) IntType; typedefchar,unsigned char) CharType; typedef TYPELIST_2(IntType,CharType) SomeType;但是我们却不能用TypeAt提取出SomeType中的IntType,如果我们用下面的代码得到的结果将不是我预期的:
TypeAt<SomeType,0>::Result我们也许希望得到一个IntType,但是程序会返回一个int给我们,这是因为TypeList类似链表的递归定义会把SomeType展开成
TYPELIST_4(int,unsigned int, char,unsigned char)对上面这个TypeList求取第一个类型当然就是int。这曾经把我弄得哭笑不得,或许自己再写一些代码把其中的IntType封装一下再存入进去能解决问题,但是为此我花费了若干天时间研究该怎么写这个封装代码,而且越到后面越是被其复杂的递归弄得晕头转向。
于是我一不做二不休,直接抛弃TypeList,自己设计了TypeVector。
4.2 一个似曾相识却又崭新的世界
为了让TypeVector能存储其自身,我必须想办法抛弃递归定义的技法,由此
我想到了下面的代码:
//这里我们只设置了7个类型,你可以写更多,但是在我后面的程序用不了那么多 template< typename Type0 = NullType,typename typenametypename typenametypename typename Type6 = NullType > struct TypeVector { typedef typedef typedef typedef typedef typedef typedef };TypeVector的形态和TypeList形成了鲜明对比,TypeVector更为简洁,而且最重要的是不需递归就可以直接从中取出任意一个类型。这个形态很像数组的定义,不过不一样的是它是用来存储类型的。
4.3 TypeVector的算法
当我实作出TypeVector之前完全没有想到会有如此大的收获,因为我仅仅是
能存储其自身而创作的它,可是我却发现其算法是如此简洁明了,和TypeList形成了鲜明对比,在定制算法时候很多都是复制粘贴的操作,虽然代码量看似很多,但是你肯定不用花很多时间在调试代码的正确性上。
为了能兼容使用TypeList的程序,我采用了和TypeList同样的算法命名。
4.3.1 求取TypeVector长度
现在求取TypeVector的长度变得异常简单,只是一些模板的偏特化而已,一
眼就可看懂其算法思想。
//******************************************************************** //名称: Length //作用: 求取TypeVector中类型个数 //输入(模板参数): TVector,需要求取长度的TypeVector。 //输出: Length<TVector>::value 求取长度值 //算法思想: // 根据TypeVector的特化方式判断出其类型个数。 //******************************************************************** template<typename TVector> struct template<> struct Length<TypeVector<> > { enum }; template<typename struct Length<TypeVector<T0> > { enum }; template<typename T0,typename struct Length<TypeVector<T0,T1> > { enum }; template<typename T0,typename T1,typename struct Length<TypeVector<T0,T1,T2> > { enum }; template<typename T0,typename T1,typename T2,typename struct Length<TypeVector<T0,T1,T2,T3> > { enum }; template<typename T0,typename T1,typename T2,typename T3,typename struct Length<TypeVector<T0,T1,T2,T3,T4> > { enum }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename struct Length<TypeVector<T0,T1,T2,T3,T4,T5> > { enum }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct Length<TypeVector<T0,T1,T2,T3,T4,T5,T6> > { enum };
使用时候也是和TypeList一样的使用方法:
Length< TypeVector<int,unsigned int, char,unsigned char> >::value;4.3.2 求TypeVector某个索引值处的类型
其实由于TypeVector类似数组的结构,我们可以很轻易的写出下面这个码:
typedefint,unsigned int,long int> IntType;当我们要取用里面索引值第1个型别时候可以这样写:
typedef IntType::Type1 MyType;不过为了兼容TypeList的程序,我们还是要提供同样的接口,哪怕看似无用的。不过即便是这样,还是能从中学到一些神奇的技巧,请你别跳过这,耐心往下看。
//******************************************************************** //名称: TypeAt //作用: 求取TypeVector中某索引值处的类型 //输入(模板参数): TVector,需要求取的TypeVector;i,索引值。 //输出: TypeAt<TVector,i>::Result 求取长度值 //算法思想: // 根据i数值不同做出不同的偏特化体 //******************************************************************** templatetypename TVector , unsigned int struct TypeAt { typedef }; templatetypename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,0> { typedef }; templatetypename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,1> { typedef }; templatetypename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,2> { typedef }; templatetypename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,3> { typedef }; templatetypename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,4> { typedef }; templatetypename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,5> { typedef }; templatetypename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,6> { typedef };写完上面的代码以后我本没太多想法,后又得到网上一位前辈(网名Intercessor)提示发现其实一切都可以变得更酷,利用宏定义在编译期执行的动作,可以简化很多重复的代码。简化后的代码像这样:
//给一个宏定义,用##(连接符)连接序号,最后就可以做出所有偏特化体 #define template <typename T0,typename T1,typename ,typename T3,typename T4,typename T5,typename T6> / struct / { / typedef T##atPos Result; / }; templatetypename TVector , unsigned int struct TypeAt { typedef }; //后面就可以利用宏定义写出重复的代码。 META_TYPEAT(0) META_TYPEAT(1) META_TYPEAT(2) META_TYPEAT(3) META_TYPEAT(4) META_TYPEAT(5) META_TYPEAT(6)
当我们需要查找某个索引值处的类型时候,程序是这样的。
typedefint,unsigned int,long int> IntType; typedef TypeAt<IntType,1>::Result MyType;
4.3.3 向TypeVector末端添加类型
类似前面的算法,向末端添加一个类型就相当于重新定义一个TypeVector并
返回,主要技巧还是模板的偏特化,下面给出代码:
//******************************************************************** //名称: Append //作用: 向TypeVector末端添加类型 //输入(模板参数): TVector,需要添加的TypeVector;T,添加类型。 //输出: Append<TVector,T>::Result 添加后得到的TypeVector //算法思想: // 根据TypeVector长度不同做出不同的偏特化体 //******************************************************************** template<typename TVector, typename T> struct template<typename struct Append<TypeVector<>,T> { typedef }; template<typename T0,typename struct Append<TypeVector<T0>,T> { typedef }; template<typename T0,typename T1,typename struct Append<TypeVector<T0,T1>,T> { typedef }; template<typename T0,typename T1,typename T2,typename struct Append<TypeVector<T0,T1,T2>,T> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename struct Append<TypeVector<T0,T1,T2,T3>,T> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename struct Append<TypeVector<T0,T1,T2,T3,T4>,T> { typedef }; template<typename T0,typename T1,typename T2, typename T3,typename T4,typename T5,typename struct Append<TypeVector<T0,T1,T2,T3,T4,T5>,T> { typedef };当需要向一个TypeList末尾添加一个类型的时候代码像这样:
typedef Append< TypeVector< int,unsigned int > ,long int >::Result IntType;4.3.4 删除TypeVector中某索引值所在位置的类型
之所以先介绍这个删除算法是因为另外一个删除算法(删除第一次匹配
类型)需要依赖到这个算法
这个算法同样很简单,只需要一些模板面特化技术就能实现。
//******************************************************************** //名称: ErasePose //作用: 删除TypeVector中某索引值所在类型 //输入(模板参数): TVector,需要添加的TypeVector;i,索引值。 //输出: ErasePose<TVector,i>::Result 删除后的TypeVector //算法思想: // 根据索引值i不同做出不同的偏特化体 //******************************************************************** templatetypename TVector , int struct ErasePose { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,0> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,1> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,2> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,3> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,4> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,5> { typedef }; template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,6> { typedef };用法如下代码:
typedef ErasePose< IntType,1 >::Result E1IntType;
4.3.5 得第一个匹配TypeVector中某类型的索引值
按照我们预先的希望,或者惯性思考,这个算法的代码也应该是若干重复的
模板偏特化而已,但是当实作出代码以后才发现原来没有那么简单,也许你会想到这个算法的代码类似这样:
template<typename TVector , typename struct IndexOf { enum }; template<typename struct IndexOf<TypeVector<T>,T> { enum }; template<typename T0,typename struct IndexOf<TypeVector<T0,T>,T> { enum }; //...and more...如果是这样,那当我写出这样的代码时候,将会出现错误结果。
typedefint,char,int,double> SomeType; intint>::value;
编译器看到上面代码将会提示你,它无法判断该执行哪一个模板偏特化体,因为索引0位置和索引2位置的偏特化体都有条件得到执行(偏特化与你书写的先后顺序无关),这就是所谓的模绫两可。
为了解决这个问题,我们只有能回到该死的偏特化递归去了,不过幸运的是毕竟不是每个算法都必须涉及到递归,否则我们这个TypeVector就没太多存在意义了。
下面介绍正确的IndexOf算法
//******************************************************************** //名称: IndexOf //作用: 在TypeVector中查找第一个匹配某一类型的索引值 //输入(模板参数): TVector,需要查找的TypeVector;T,所要查找的类型 //输出: IndexOf<TVector,T>::value 查找后得到的索引值 //算法思想: // 如果TVector为空,则定义value为-1 // 否则 // 如果TVector第一个类型匹配T,则定义value为0 // 否则 // 删除TypeVector的第一个类型再做索引,并且把结果value赋值临时变// 量temp。 // 如果temp为-1,则定义value为-1, // 否则 // 定义value为 temp + 1 。 //******************************************************************** templatetypename TVector , typename T> struct template<typename struct IndexOf<TypeVector<> ,T> { enum }; templatetypename structtypename { enum{ value = 0 }; }; templatetypename TVector ,typename struct IndexOf { private: enum{ temp = IndexOf<typename public: enum{ value = temp == -1 ? -1 : 1 + temp }; };如果你需要索引某类型时候,程序代码像这样的:
typedefint,char,int,double> SomeType; intint>::value;上面的index将得到0;
4.3.6 删除TypeVector中第一个匹配的类型
这个算法之所以在最后讲是因为需要用到前面算法的帮助,否则又将是让人
厌烦的模板偏特化递归调用。
//******************************************************************** //名称: Erase //作用: 删除TypeVector中查找第一个匹配的某类型 //输入(模板参数): TVector,需要删除的TypeVector;T,所要删除的类型 //输出: Erase<TVector,T>::Result 删除后得到的TypeVector //算法思想: // 求出类型T的索引位置并赋值value,然后删除value索引所在位置的类型。 //******************************************************************** template<typename TVector,typename struct Erase { enum typedef typename };
4.4 TypeVector结束语
TypeVector相信大家现在有所了解了,其以类似数组方式的存储形态使得其
支持随即读取其中某个类型,这在很大程度上简化了我们对应算法的复杂程度,而且其有一个优良的特性就是可以将TypeVector类型存储于TypeVector中,且最后可以调用IndexOf寻找出其索引值或者根据索引值调用TypeAt获得其类型,这在某些复杂类型情况下是很方便的。
但是正所谓寸有所短,尺有所长。TypeVector相对于TypeList的缺点就是维护稍显困难,当我们因为程序需要而在库文件中扩展TypeList的长度时候我们并不需要修改其算法,但是如果我们修改TypeVector最大容量以后就必须修改其对应算法,不过值得高兴的是我们可能只需要一些复制粘贴动作就可以修改了,但是即便最小的修改也需要重新编译程序,这在某些时候显得异常麻烦。
再好的算法也必须在最适合的环境下才能得到最大的效率,这也就是我同时介绍TypeList和TypeVector的关系,因为不一定其中一种就是最好的,只是看在什么环境下了。