书名: | 《自动化C++程序设计》 |
作者: | 熊春雷 |
网站: | http://www.autodev.net |
Blog: | http://blog.csdn.net/pandaxcl |
EMail: | pandaxcl@163.com |
昵称: | pandaxcl,开心 |
QQ: | 56637059 |
MSN: | pandaxcl@163.com |
版本: | 0.01 于2007/09/25 |
目标: | 所有C++爱好者 |
版权: | 本文的版权归熊春雷所有 |
代码库: | autocxx(在论坛下载) |
Warning
-
本文由熊春雷所写,绝对保证原创,在此特别严肃声明。本人简介:熊春 雷,男,1980年出生于湖北钟祥;七岁随父迁往宜昌开始学生生涯,小学和初中在 湖北宜昌樟村坪镇职工子弟学校就读;1996年考上宜昌县高中,开始三年的高中生 活;1999-2003就读于湖北大学物理系;2003-2006就读于武汉大学物理系。现就职 于盛大网络:)
-
绝对不能容忍他人说本文为他所写以及其他的侵权行为。一旦发现,一定 尽本人最大的能力以法律的形式严追到底,决不妥协。
-
引用本文,要保证本文的完整性,不可以删除此处的声明,并且务必注明出处。
Tip
-
本文编写的所有代码可以用于任何用途(包括商业用途)。
-
用于商业用途的需要在最后发布的软件中声明借鉴了本文的思想。具体事 宜可以协商解决,(代码决不收取任何费用)。
-
其他事项可以和我联系,包括技术讨论等等:)或者直接登陆网站论坛: http://www.autodev.net
Note
-
本文受到了《C++设计新思维》和《产生式编程》两本书的影响,同时也查阅了大 量的资料,从Loki库和Boost库中也吸收了不少营养,特此感谢之。
-
本文由于处于原创阶段,难免会出现各种各样的错误。代码出现错误的可能性非常 小(本来想说为零的),因为文档和代码是严格同步的,这是由VST文本的include 所保证的,代码都是测试成功之后才发布的。
-
本文所编写的代码,经过了VC2005编译器和g++编译器的测试,并且都通过了。
-
本文还没有彻底完成,算是一个初级版本,未来还将继续完善。暂时发布出来是为 了预知读者群有多少,读者越多,我的成就感越强,写作的时候也会更有动力:)
-
本文还会继续完善,欢迎各位读者的批评指正,也接受各种各样的建议,在权衡之 后以决定是否加入本书。
-
本书还没有最终完成,还会不断的进行完善,更新之后的内容将会发表于我的 网站或我的博客。所以还需要读者多多关心本文的进展:)
Contents
分析函数参数的种类
目的
本文的目的是根据给出的重载函数(on),分析出在可能的候选函数参数类型中有哪些类 型被使用了。关于这一点功能的实现还需要分四个步骤来实现:
-
先实现一个参数的重载函数的分析
-
再利用和一个参数的重载分析一样的方法实现两个参数的重载函数的分析
-
将2中的方法扩展到两个参数的自动化分析
-
对于多于两个参数的自动化分析采用3中的方法实现之
先实现一个参数的重载函数的分析
kind<9> on(...); char on(n<1>); char on(n<3>); char on(n<4>); template<class ENV,class A1>struct FILTER { typedef n<sizeof(on(A1()))!=sizeof(kind<9>)> type; }; // 把所有的可能类型列举出来 typedef mkps<n<1>,n<2>,n<3>,n<4>,n<5>,n<6>,n<7>,n<8>,n<9> >::type EPS; typedef filter<EPS,nil,FILTER>::type USED; typedef mkps<n<1>,n<3>,n<4> >::type EXPECTED; static_assert_same<EXPECTED,USED>();
从上面的代码可以看出,对于一个参数的情况,仅仅用简单的过滤就可以了。此外,上 面的代码处理的是一个全局域中的函数(on)的参数种类问题!实际上这种分析全局域 中的函数参数种类的情况并不多见,常见的是分析类域中的函数参数种类的问题!所以 本文的重点就放在分析类域中的函数参数种类的问题上面:
struct A// 用类域把函数封装起来 { kind<9> on(...); char on(n<1>); char on(n<3>); char on(n<4>); }; template<class ENV,class H>struct FILTER;// filter需要的格式 template<class Type,class Kind,class H>struct FILTER<p<Type,p<Kind> >,H> { enum{i=sizeof(Type().on(H()))!=sizeof(Kind)}; typedef n<i> type; }; // 把所有的可能类型列举出来 typedef mkps<n<1>,n<2>,n<3>,n<4>,n<5>,n<6>,n<7>,n<8>,n<9> >::type EPS; typedef filter<EPS,mkps<A,kind<9> >::type,FILTER>::type USED; typedef mkps<n<1>,n<3>,n<4> >::type EXPECTED; static_assert_same<EXPECTED,USED>();
从上面的代码可以看出:将函数封装到了类(A)域中,这样使用起来就方便多了。
上面的代码采用的基本方法是:依次测试每一个传入的参数,能够匹配传入参数的函数的 返回值类型不是kind<9>,而不匹配传入参数的函数,就会匹配省略号参数的函数,该函数 返回值类型就是kind<9>。把不匹配省略号参数函数的类型过滤出来,就是所需要的结果。
接下来的分析两个及两个参数以上的函数,也是采取的这种方法!从上面的代码中,还可 以看出,函数的名称被固定为on了!这是没有必要的,但是为了能够把函数名称和功能代 码分隔开,还需要提供一个FUNCTION模板:
struct A// 用类域把函数封装起来 { kind<9> on(...); char on(n<1>); char on(n<3>); char on(n<4>); }; // 下面就是隔离出来的FUNCTION模板,可以通过宏的方式修改函数名称和类名 template<class Type,class Kind,class ArgPS>struct FUNCTION; template<class Type,class Kind,class A0>struct FUNCTION<Type,Kind,p<A0> > { enum{i=sizeof(Type().on(A0()))!=sizeof(Kind)}; typedef n<i> type; }; // 下面是分析代码的功能代码,已经和函数名称和类名分开了 template<class ENV,class H>struct FILTER;// filter需要的格式 template<class Type,class Kind,class H>struct FILTER<p<Type,p<Kind> >,H> { typedef typename FUNCTION<Type,Kind,p<H> >::type type; }; // 把所有的可能类型列举出来 typedef mkps<n<1>,n<2>,n<3>,n<4>,n<5>,n<6>,n<7>,n<8>,n<9> >::type EPS; typedef filter<EPS,mkps<A,kind<9> >::type,FILTER>::type USED; typedef mkps<n<1>,n<3>,n<4> >::type EXPECTED; static_assert_same<EXPECTED,USED>();
再利用和一个参数的重载分析一样的方法实现两个参数的重载函数的分析
函数模板声明(function.hpp):
// 判定指定参数类型串中的类型 template<class Type,class Kind,class ArgPS>struct FUNCTION;
过滤器(filter.hpp):
template<class Init,class H>struct FILTER; template<class Type,class Kind,class Init,class H> struct FILTER<p<Type,p<Kind,p<Init> > >,H> { // Init的长度就是函数参数的数量 typedef typename length<Init>::type Size; // 清理出给定的参数中的有效参数 typedef typename partition<Init,any>::type TMP1; // 尝试追加新的参数 typedef typename join<TMP1,p<H> >::type TMP2; // 用any补全参数到Size数量 typedef typename resize<TMP2,Size,any>::type TMP3; // 最后过滤出存在函数的参数列表 typedef typename FUNCTION<Type,Kind,TMP3>::type type; };
映射(map.hpp):
template<class Init,class H>struct MAP { // 将H附加到Init的末尾,形成新的参数列表 typedef typename join<Init,p<H> >::type type; };
收缩(reduce.hpp):
template<class Init,class H>struct REDUCE; template<class Type,class Kind,class EPS,class Init,class H> class REDUCE<p<Type,p<Kind,p<EPS,p<Init> > > >,H> { // 准备好的初始化filter参数 typedef p<Type,p<Kind,p<H> > > TMP0; // 过滤出新的参数类型 typedef typename filter<EPS,TMP0,FILTER>::type TMP1; // 清理出所有的给定的有效参数 typedef typename partition<H,any>::type TMP2; // 将接着存在的参数附加到上一次的参数列表之后 typedef typename map<TMP1,TMP2,MAP>::type TMP3; // 将这样的所有参数列表收集起来 typedef typename join<Init,TMP3>::type TMP4; public: // 为了reduce能够继续执行,需要将结果保存为reduce制定的格式 typedef p<Type,p<Kind,p<EPS,p<TMP4> > > > type; };
参数初始化(initialize.hpp):
template<class Size,class H>struct INITIALIZE { typedef typename resize<p<H>,Size,any>::type type; };
串接处理多个参数(next.hpp):
template<class Size,class H>struct NEXT { typedef typename resize<H,Size,any>::type type; };
所有的函数模板声明(function1.hpp,function2.hpp,function3.hpp):
template<class Type,class Kind,class A0> struct FUNCTION<Type,Kind,p<A0> >// 一个参数的FUNCTION { enum{i=(sizeof(Type().operator()(A0()))!=sizeof(Kind))}; typedef n<i> type;// 必须将i隔离成为枚举类型,保持编译器之间的兼容性 }; template<class Type,class Kind,class A0,class A1> struct FUNCTION<Type,Kind,p<A0,p<A1> > >// 两个参数的FUNCTION { enum{i=(sizeof(Type().operator()(A0(),A1()))!=sizeof(Kind))}; typedef n<i> type;// 必须将i隔离成为枚举类型,保持编译器之间的兼容性 }; template<class Type,class Kind,class A0,class A1,class A2> struct FUNCTION<Type,Kind,p<A0,p<A1,p<A2> > > >// 三个参数的FUNCTION { enum{i=(sizeof(Type().operator()(A0(),A1(),A2()))!=sizeof(Kind))}; typedef n<i> type;// 必须将i隔离成为枚举类型,保持编译器之间的兼容性 }; //... 更多的函数
下面是上面的处理多个函数参数的测试用例:
struct A// 两个参数的被测试类型 { undefined operator()(...); char operator()(n<3>,n<5>); char operator()(n<3>,n<2>); char operator()(n<4>,n<7>); char operator()(n<4>,n<6>); char operator()(n<6>,n<3>); char operator()(n<6>,n<4>); char operator()(n<6>,n<4>,n<9>);// 即使存在其它参数数量也没有问题 }; typedef mkps<n<1>,n<2>,n<3>,n<4>,n<5>,n<6>,n<7>,n<8>,n<9> >::type EPS; // 探测出两个参数的类型列表 typedef map<EPS,n<2>,INITIALIZE>::type INIT;// 将所有的参数初始化 typedef reduce<INIT,p<A,p<undefined,p<EPS,p<nil> > > >,REDUCE>::type USED; //typedef reduce<INIT,mkps<A,undefined,EPS,nil>::type,REDUCE>::type USED; typedef mkps< mkps<n<3>,n<2> >::type, mkps<n<3>,n<5> >::type, mkps<n<4>,n<6> >::type, mkps<n<4>,n<7> >::type, mkps<n<6>,n<3> >::type, mkps<n<6>,n<4> >::type >::type EXPECTED;// 期待的类型串,注意,都是按照EPS里面列举的顺序排列的 static_assert_same<EXPECTED,at<USED,3>::type>();
struct B// 三个参数的被测试类型 { undefined operator()(...); char operator()(n<3>,n<5>,n<5>); char operator()(n<3>,n<2>,n<4>); char operator()(n<4>,n<7>,n<3>); char operator()(n<4>,n<6>,n<2>); char operator()(n<6>,n<3>,n<1>); char operator()(n<6>,n<4>,n<7>); char operator()(n<6>,n<4>);// 即使存在其它参数数量也没有问题 }; typedef mkps<n<1>,n<2>,n<3>,n<4>,n<5>,n<6>,n<7>,n<8>,n<9> >::type EPS; // 探测出三个参数的类型列表 typedef map<EPS,n<3>,INITIALIZE>::type INIT0;// 将所有的参数初始化 typedef reduce<INIT0,p<B,p<undefined,p<EPS,p<nil> > > >,REDUCE>::type USED0; typedef mkps< mkps<n<3>,n<2> >::type, mkps<n<3>,n<5> >::type, mkps<n<4>,n<6> >::type, mkps<n<4>,n<7> >::type, mkps<n<6>,n<3> >::type, mkps<n<6>,n<4> >::type >::type EXPECTED0;// 期待的类型串,注意,都是按照EPS里面列举的顺序排列的 static_assert_same<EXPECTED0,at<USED0,3>::type>(); typedef map<at<USED0,3>::type,n<3>,NEXT>::type INIT1;// 将所有的参数初始化 typedef reduce<INIT1,p<B,p<undefined,p<EPS,p<nil> > > >,REDUCE>::type USED1; typedef mkps< mkps<n<3>,n<2>,n<4> >::type, mkps<n<3>,n<5>,n<5> >::type, mkps<n<4>,n<6>,n<2> >::type, mkps<n<4>,n<7>,n<3> >::type, mkps<n<6>,n<3>,n<1> >::type, mkps<n<6>,n<4>,n<7> >::type >::type EXPECTED1;// 期待的类型串,注意,都是按照EPS里面列举的顺序排列的 static_assert_same<EXPECTED1,at<USED1,3>::type>();
从上面的代码可以很容易看出,对于多于两个参数的函数的参数类型的测定可以采用这里 类似的方法很容易的得出,只是这里的代码只说明了原理,还不能以库的形式提供,每次 使用的时候还需要对这里给出的代码进行修改,接下来的几节中将会针对这里的这种情况 进行讨论,也就本书的核心:自动化的实现。
将2中的方法扩展到多个参数的自动化分析
所谓的自动化的方法就是把不能以库的形式提供的代码尽可能的以库的形式提供的方法, 同时还要保证有一致的应用界面,使得应用较为简洁。在这里将会详细的讨论将上面的原 理进行自动化封装。
在上面的代码中,已经将所有的重载函数封装到了一个类中,这样就将被测试对象具体化 了。同时还应当注意到:被测试函数的参数的数量应当是已知的,这个可以通过上一节的 测试函数参数的数量的功能得到。
为了将上面的测试代码能够以库的形势提供,还需要解决下面的几个问题:
-
提供所有的参数数量的FUNCTION。这一点不可满足,通常只能够给定一个比较大的值, 但是也足够了
-
前面代码里面的通用函数(参数为省略号的函数)的返回值(undefined)也应该和前 面判断函数参数数量的方案一样,也应当泛型化
-
将测量两个以上的参数的过程封装起来,给出一个比较简单的界面
接下来的任务是:使上面的封装更加通用化!
那么一个直接的问题就摆在了我们的面前: 通用化的封装的结果是什么 ?
为了尽可能的保证实用性,我们假定:
-
参数之间的前后顺序也需要保留下来
-
最后的结果就是一个类似于XML结构的 层级结构
有了这两条假定之后,目标就非常明确了,下面就来尝试对上面的简单封装的结果再次进 行封装:
下面就是处理一个参数到九个参数封装方案:
template<class ePS,class A,class K,template<class,class,class>class FUNCTION,class N=n<10> > class function_parameter_kinds// 把EPS改为ePS,可以增强编译器之间的兼容性 { #include "filter.hpp" #include "map.hpp" #include "reduce.hpp" #include "initialize.hpp" #include "next.hpp" // 下面是静态循环语句,实现了从两个参数的函数开始到N个参数的测试功能 template<class Init,class i>struct Statement { typedef typename map<typename at<Init,3>::type,N,NEXT>::type INIT; typedef typename reduce<INIT,p<A,p<K,p<ePS,p<nil> > > >,REDUCE>::type type; }; template<class Init>struct Statement<Init,n<2> > { typedef typename map<ePS,N,INITIALIZE>::type INIT; typedef typename reduce<INIT,p<A,p<K,p<ePS,p<nil> > > >,REDUCE>::type type; }; // 注意这里的区间范围是[n<2>,END),和STL的规范相兼容 typedef typename increase<N>::type END; typedef typename loop<n<2>,END,increase,nil,Statement>::type USED; public: typedef typename at<USED,3>::type type;// 这才是需要的结果 };
Tip
-
无以规矩,何以方圆。得到任何好处的同时当然也要付出一定的代价:)
在给出示例之前还需要强调一下!为了扩展性,将函数的名字采用分析函数参数数量的 AUTOCXX_DEFINE_FUNCTION宏进行了封装,使得可以自由定义函数名称。在这里是仿函数:
AUTOCXX_DEFINE_FUNCTION(operator(),FUNCTOR)
下面是采用function_parameter_kinds针对上面的类A的仿函数参数分析的示例代码:
typedef mkps<n<1>,n<2>,n<3>,n<4>,n<5>,n<6>,n<7>,n<8>,n<9> >::type EPS; typedef function_parameter_kinds<EPS,A,undefined,FUNCTOR,n<2> >::type ARGS; typedef mkps< mkps<n<3>,n<2> >::type, mkps<n<3>,n<5> >::type, mkps<n<4>,n<6> >::type, mkps<n<4>,n<7> >::type, mkps<n<6>,n<3> >::type, mkps<n<6>,n<4> >::type >::type EXPECTED;// 期待的类型串,注意,都是按照EPS里面列举的顺序排列的 static_assert_same<EXPECTED,ARGS>();
下面是采用function_parameter_kinds针对上面的类B的仿函数参数分析的示例代码:
typedef mkps<n<1>,n<2>,n<3>,n<4>,n<5>,n<6>,n<7>,n<8>,n<9> >::type EPS; typedef function_parameter_kinds<EPS,B,undefined,FUNCTOR,n<3> >::type ARGS; typedef mkps< mkps<n<3>,n<2>,n<4> >::type, mkps<n<3>,n<5>,n<5> >::type, mkps<n<4>,n<6>,n<2> >::type, mkps<n<4>,n<7>,n<3> >::type, mkps<n<6>,n<3>,n<1> >::type, mkps<n<6>,n<4>,n<7> >::type >::type EXPECTED;// 期待的类型串,注意,都是按照EPS里面列举的顺序排列的 static_assert_same<EXPECTED,ARGS>();
从上面的代码可以看出,我们已经成功的实现了前面一节的方案的封装,功能上面已经达 到了我们先前所讨论的所有功能了:)