C++自动化(模板元)编程基础与应用
作者:Ackarlix
挨踢网【中文IT技术社区】http://www.aitic.net
#if 0
大家好,在这一类的文章中将会系统的介绍模板元编程的相关基础知识。最主要的是
这类文章的目的就是让不熟悉模板元的C++迷们尽快的进入到模板元的奇妙世界里面来,所
以每一篇文章都将只会讨论一个话题,尽可能的把这个话题说清楚明白就可以了。
好了,言归正传。大家都知道C++是一们计算机语言,这一点也没错,但是你知道C++
里面还包含了另外一种子语言么?呵呵,恐怕知道的人就不多了,会用的人就更少了。但
是既然经过了这么多年的发展,C++语言里面出现了这种子语言(模板)自然有它的根源的
,这一点并不是本文中将要讨论的,如果想了解C++的发展例程,可以参见相关的文献。
在本文中将要说明的问题是:为什么说C++模板是一种语言呢?
为了回答这个问题,首先需要考虑一下什么是计算机语言,关于这个精确的定义,很
多的计算机基础教程上都有,在这里我给出一种比较窄的定义:
能够在计算机上表达选择结构,循环结构,同时能够进行进行整数的四则运算的体系
就是一种计算机语言。
很显然,C++自然是一种计算机语言了,还有Basic,Fortran,Pascal等等都是计算机语
言。之所以讨论这么多的概念问题是为了说明:如何证明C++的模板语法是一种计算机语言
。又因为模板是C++语言的一个元素,所以又可以将C++模板语法称为C++的二级语言或者子
语言。在本文中将会通过使用模板分别实现整数四则运算,选择结构以及循环结构来证明
C++模板语法构成了一个完整的计算机语言。
另外特别值得注意的是,因为C++的模板语言是在编译器编译的时候完成的,所以又称
为静态语言,通常的C++语言又称为动态语言或者运行时语言。正是因为模板语言是在编译
期完成的,所以我们可以借助于这种编译期的计算实现代码自动生成的目的,从而实现C++
自动化编程。这是后续的文章中会详细讨论的。
首先看看,模板是如何完成编译期四则计算的。
#endif
#ifdef CODE1//编译期四则计算的示例代码
#include <iostream>
template<size_t i,size_t j> struct Add { enum{value = i+j}; };
template<size_t i,size_t j> struct Sub { enum{value = i-j}; };
template<size_t i,size_t j> struct Mul { enum{value = i*j}; };
template<size_t i,size_t j> struct Div { enum{value = i/j}; };
int main()
{
std::cout << "4+2=" << Add<4,2>::value << std::endl;
std::cout << "4-2=" << Sub<4,2>::value << std::endl;
std::cout << "4*2=" << Mul<4,2>::value << std::endl;
std::cout << "4/2=" << Div<4,2>::value << std::endl;
//为了证明上面的计算是在编译期进行的,我们编写下面的代码测试
//将模板值作为数组定义时使用的参数就可以证明是在编译期执行的计算:)
int a[Add<4,2>::value];//这么定义并没有错
int b[Sub<4,2>::value];//这么定义并没有错
int c[Mul<4,2>::value];//这么定义并没有错
int d[Div<4,2>::value];//这么定义并没有错
std::cout << sizeof(a)/sizeof(int) << std::endl;
std::cout << sizeof(b)/sizeof(int) << std::endl;
std::cout << sizeof(c)/sizeof(int) << std::endl;
std::cout << sizeof(d)/sizeof(int) << std::endl;
return 0;
}
#endif//CODE1
//程序运行结果如下所示:
/*******************************************************************************
4+2=6
4-2=2
4*2=8
4/2=2
6
2
8
2
*******************************************************************************/
#if 0
从代码CODE1中可以看出使用整型模板参数的模板是可以实现编译期计算的,在这里,
证明了这个计算过程是在编译期完成的。
好了,现在看看如何使用C++模板实现选择结构,见代码CODE2:
#endif
#ifdef CODE2//编译期实现选择的示例代码
#include <iostream>
template <bool Condition,class Then,class Else>
struct IF
{
typedef Then result;//将Then类型作为条件为真的返回值(返回值为类型)
};
template<class Then,class Else>
struct IF<false,Then,Else>
{
typedef Else result;//将Else类型作为条件为假的返回值(返回值为类型)
};
//为了测试这个IF选择结构,需要下面的两个类型定义:
struct True {static void Print(){std::cout << "真" << std::endl;}};
struct False{static void Print(){std::cout << "假" << std::endl;}};
int main()
{
IF<1==1,True,False>::result::Print();
IF<1!=1,True,False>::result::Print();
return 0;
}
#endif//CODE2
//程序运行结果如下所示:
/*******************************************************************************
真
假
*******************************************************************************/
#if 0
从CODE2中可以看出,这里操作的对象是类型,而CODE1中的操作对象是整数,到了这
里可以总结如下:C++模板元编程中的操作对象只有两种,一种是整形数,包括bool,char
,int,long,(signed unsigned)都可以,它们都可以当作整数使用,赋值和保存结果的
方式都是通过枚举变量来实现;另一种就是类型了,赋值和保存结果都是通过typedef来实
现的。例如CODE2中将IF的选择结果以Result的方式保存作为结果就是通过typedef实现的。
再来看看循环结构:
#endif
#ifdef CODE3//编译期实现循环的示例代码
#include <iostream>
//为了简单采用一个阶乘作为例子,因为如果用普通的C++语法来实现阶乘函数的话需要
//一个循环结构的,这里采用模板递归的方式实现了这种阶乘,也就实现了一种特殊的
//循环结构。
template<size_t n> struct Power {
enum{value=n*Power<n-1>::value}; //循环递归过程
};
template<> struct Power<0> {
enum{value=1}; //0的阶乘是1,也是循环的终止条件
};
int main()
{
int a[Power<5>::value];//同样用数组参数来判断是否在编译期完成计算
std::cout << sizeof(a)/sizeof(int) << std::endl;
return 0;
}
#endif//CODE3
//程序运行结果如下所示:
/*******************************************************************************
120
*******************************************************************************/
#if 0
从CODE3中我们可以看出Power是通过模板递归的方式实现循环的,而且这个循环过程
是在编译期完成的。到了这里可以总结出:C++模板元编程中实现循环的方式只有一种,那
就是模板递归实现循环。虽然这里的Power的循环不怎么直接,但是它确确实实是一个循环
结构,只不过是一个非常特殊的循环结构。实际上采用模板递归的方法可以实现普通C++语
法里面的for循环,while循环,do-while循环这些通用的循环结构。
到目前为止,已经成功的证明了C++模板是一个完整的计算机语言。既然是一门语言,
当然可以做许许多多的事情,这就在于每个人的发挥了。好了,在本文的最后给出一个通
用的LOOP循环作为本文的结束,这个LOOP循环可以进行简单的循环算法设计了,下面的例
子中将会说明这一点:
#endif
#ifdef CODE4
#include <iostream>
template<size_t n> void print()
{//这里的n是编译期的结果,可以用来定义数组的
int a[n+1];//这么做是为了证明n是编译期常量,同时避免出现零个元素的数组
std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
template <size_t i>struct LOOP
{
static void execute(){LOOP<i-1>::execute();print<i>();}
};
template <>struct LOOP<0>//循环终止条件
{
static void execute(){print<0>();}
};
int main()
{
LOOP<5>::execute();
return 0;
}
#endif//CODE4
//程序运行结果如下所示:
/*******************************************************************************
0 1 2 3 4 5
*******************************************************************************/
#if 0
从CODE4中可以看出,这个静态LOOP循环是一个相对来说通用的循环代码,只需要将自
己的功能代码写入到一个函数(print)中就可以实现静态循环了,更重要的是,这个静态
LOOP循环实现了静态代码和动态代码的连接,因此用途更加广泛,主要可以用来产生代码
。关于如何产生代码,以及如何使用将是本类文章的后续文章讨论的内容。
本章完。
在下一章里面将会详细的讨论这里的LOOP循环并且一步一步的进行演化将这个LOOP循
环写得越来越通用,这里面是一个学习过程,也是理解这里的静态循环的一个理解过程。
(敬请关注!)
未完,待续...
#endif
#if 0
在上一篇文章的最后提到了一个相对来说通用一点的LOOP循环,下面还是将上一篇文
章中的LOOP循环代码复制如下:
#endif
#ifdef CODE1
#include <iostream>
template<size_t n> void print()
{//这里的n是编译期的结果,可以用来定义数组的
int a[n+1];//这么做是为了证明n是编译期常量
std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
template <size_t i>struct LOOP
{
static void execute(){LOOP<i-1>::execute();print<i>();}
};
template <>struct LOOP<0>//循环终止条件
{
static void execute(){print<0>();}
};
int main()
{
LOOP<5>::execute();
return 0;
}
#endif//CODE1
//程序运行结果如下所示:
/*******************************************************************************
0 1 2 3 4 5
*******************************************************************************/
#if 0
现在所需要考虑的问题是怎么将上面的print模板函数书写得更通用些,因为每定义一个
新的print函数就需要重新书写两个LOOP模板,为了避免这种重复代码得书写,在此很容易想
到的是将这个print模板函数作为模板参数传递,好了让我们开始新的尝试吧。具体得示例代
码如下所示:
#endif
#ifdef CODE2
#include <iostream>
//下面的两个模板就比CODE1里面的LOOP模板通用多了
template <template<size_t>class Function,size_t i> struct LOOP
{
static void execute()
{
LOOP<Function,i-1>::execute();
Function<i>::execute();
}
};
template <template<size_t>class Function> struct LOOP<Function,0>
{
static void execute()
{
Function<0>::execute();
}
};
//为了模板化必须将原来的输出函数做成一个模板结构体
template<size_t n> struct Function
{
static void execute()
{//这里的n是编译期的结果,可以用来定义数组的
int a[n+1];//这么做是为了证明n是编译期常量
std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
};
//下面是测试代码
int main()
{
LOOP<Function,5>::execute();
return 0;
}
#endif//CODE2
//程序运行结果如下所示:
/*******************************************************************************
0 1 2 3 4 5
*******************************************************************************/
#if 0
从CODE2的代码中可以看出,CODE2中的LOOP循环将CODE1中的print模板函数模板参数
化之后就使得CODE2中的LOOP更通用一些了,在使用的时候仅仅只是需要按照Function的规
范写一个模板结构就可以了,不必再书写重复结构的LOOP了,从而精简了使用时需要编写
的代码量。但是我们注意到结果并不复合STL的前闭后开的习惯,我们也不能很容易的理解
测试代码main函数中的LOOP<Function,n>的意义了,为了避免使用中的错误必须规定好它
的明确的意义:
(1)从0到n循环执行Function,并且是前闭后开的习惯
(2)将Function执行n次,每次的整型参数从0递增1
为了使CODE2中的LOOP循环表达的是这两个意义,必须对CODE2中的LOOP循环进行改写
改写候的代码如下所示:
#endif
#ifdef CODE3
#include <iostream>
//下面的两个模板就比CODE1里面的LOOP模板通用多了
template <template<size_t>class Function,size_t i> struct LOOP
{
static void execute()
{
LOOP<Function,i-1>::execute();
Function<i-1>::execute();
}
};
template <template<size_t>class Function> struct LOOP<Function,0>
{
static void execute(){}
};
//为了模板化必须将原来的输出函数做成一个模板结构体
template<size_t n> struct Function
{
static void execute()
{//这里的n是编译期的结果,可以用来定义数组的
int a[n+1];//这么做是为了证明n是编译期常量
std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
};
//下面是测试代码
int main()
{
//下面的这行代码可以按照上面的两种意义直观的理解为:
//(1)从0到5循环执行Function,范围[0,5)
//(2)将Function执行5次,每次的整型参数从0递增1
LOOP<Function,5>::execute();
return 0;
}
#endif//CODE3
//程序运行结果如下所示:
/*******************************************************************************
0 1 2 3 4
*******************************************************************************/
#if 0
从CODE3的运行结果来看确实满足了我们所需要表达的明确的意义,因此代码理解起来
就非常直观了。
从CODE3的代码中我们还是发现LOOP的起始参数固定为0,这一点实际上也是可以进一
步改进的。下面我们就来看看如何使得起始模板参数也可以指定的,见代码CODE4:
#endif
#ifdef CODE4
#include <iostream>
//加入一个外覆层来传递额外的模板参数
template <template<size_t>class Function,size_t start,size_t finish>
struct LOOP
{
static void execute()
{
LOOP_BODY<finish,true>::execute();
}
private:
//引入了一个布尔型的模板参数用来确定循环的终止条件
template <size_t i,bool> struct LOOP_BODY
{
static void execute()
{
LOOP_BODY<i-1,(i-1>start)>::execute();
Function<i-1>::execute();
}
};
//循环的终止语句,停止递归以结束循环
template <size_t i> struct LOOP_BODY<i,false>
{
static void execute(){}
};
};
//为了模板化必须将原来的输出函数做成一个模板结构体
template<size_t n> struct Function
{
static void execute()
{//这里的n是编译期的结果,可以用来定义数组的
int a[n+1];//这么做是为了证明n是编译期常量
std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
};
//下面是测试代码
int main()
{
//下面的这行代码可以直观的理解为:
//(1)从1到5循环执行Function,范围[1,5)
LOOP<Function,1,5>::execute();
//将两个输出分成两行输出
std::cout << std::endl;
//(1)从2到5循环执行Function,范围[2,5)
LOOP<Function,2,5>::execute();
return 0;
}
#endif//CODE4
//程序运行结果如下所示:
/*******************************************************************************
1 2 3 4
2 3 4
*******************************************************************************/
#if 0
从CODE4中的代码我们成功的通过添加了一层外敷类实现了循环的初始值设置,但是我们
还是应当注意CODE4中的循环步长并不能够指定,所以在这里我们通过类似的方法来实现步长
的指定。详细代码见CODE5所示:
#endif
#ifdef CODE5
#include <iostream>
//加入一个外覆层来传递额外的模板参数
template <template<size_t>class Function,size_t start,size_t finish,size_t step>
struct LOOP
{
static void execute()
{
//为了能够正确的计算出实际的循环终止变量,需要对给定的终止变量
//进行计算,以满足正确的循环语义
LOOP_BODY<(finish/step*step+start),true>::execute();
}
private:
//引入了一个布尔型的模板参数用来确定循环的终止条件
template <size_t i,bool> struct LOOP_BODY
{
static void execute()
{
LOOP_BODY<i-step,(i-step>start)>::execute();
Function<i-step>::execute();
}
};
//循环的终止语句,停止递归以结束循环
template <size_t i> struct LOOP_BODY<i,false>
{
static void execute(){}
};
};
//为了模板化必须将原来的输出函数做成一个模板结构体
template<size_t n> struct Function
{
static void execute()
{//这里的n是编译期的结果,可以用来定义数组的
int a[n+1];//这么做是为了证明n是编译期常量
std::cout << sizeof(a)/sizeof(int)-1 << " " ;
}
};
//下面是测试代码
int main()
{
//下面的这行代码可以直观的理解为:
//(1)从1到10循环执行Function,范围[1,10),步长2
LOOP<Function,1,10,2>::execute();
//将两个输出分成两行输出
std::cout << std::endl;
//(1)从2到10循环执行Function,范围[2,10),步长3
LOOP<Function,2,10,3>::execute();
//暂时还不能允许下面的逆向调用方式
//LOOP<Function,10,2,-3>::execute();
return 0;
}
#endif//CODE5
//程序运行结果如下所示:
/*******************************************************************************
1 3 5 7 9
2 5 8
*******************************************************************************/
#if 0
从CODE5的代码中可以看出明显存在一个缺陷:并不能够实现逆向的循环操作。这在类
型操作中并不是什么缺陷!因为在后续的章节的代码产生机制中并不需要逆向的循环操作
,所以为了使得本文尽量的简单,在这里并不讨论这个问题,这个问题留给读者思考,如
果在本类文章的后续章节中需要这个机制,那么我会在需要的地方实现之。
从CODE5的运行结果来看,CODE5的实现已经满足了我们日常编码的需要了,实际上用
这里介绍的方法还可以非常容易的实现许许多多的循环语句,包括FOR循环,WHILE循环等
等,但是需要注意的是:采用模板元编程主要是用来实现自己的功能代码的,使用这里介
绍的循环代码已经可以满足后面进行代码生成的需要了。所以本文的静态循环代码介绍也
可以到此打住了,不过各位有兴趣的话,可以和我联系,我非常愿意和您讨论这些话题:)
本章完。
在下一章里将会介绍代码的生成的机制,并通过解决一些简单的问题来说明代码生成
为什么可以实现自动化过程。(敬请关注!)
未完,待续...
#endif
#if 0
在前面的两章里面讨论了C++模板元作为C++的一门二级语言的问题,并给出了常用的
程序设计语言的语素的实现,是一个完备的体系。总的来说,前面的章节里面是采用了下
面的方法来实现这些语素的:
(1)整数计算结果通过enum变量进行保存
(2)类型计算结果通过typedef进行保存
(3)?:运算符可用来实现静态整型表达式的选择功能
(4)模板特化可用来实现静态类型表达式的选择功能
(5)模板递归可用来实现静态循环,循环变化元素只能够是整数
(6)通过整数可以映射到类型,所以循环变化元素也可以间接为类型
这一章里面我们将要讨论另外的问题,所采用的方法也是这些方法。那么本文将要讨
论的问题是:
如何实现类型循环,也就是上面总结出来的第(6)种技巧。
关于这一点的讨论,我认真参考了<<Modern C++ Design>>一书的Typelist,在本文中
将会以cons来表达类型列表的概念,并对<<Modern C++ Design>>一书的Typelist相关的操
作进行精简,得到我们将会在生成代码的过程中使用的模板元函数。不用的根本不会考虑
,所以为了使撤销和重做库尽可能的独立些,所以我不采用Loki库,这样使得该撤销和重
做库的安装比较简单。
为此,首先实现一个类型串类型名叫cons,代码如下:
#endif
#ifdef CODE_NOTE
//cons的实现,采用和STL类似的类型命名方式
template <class FirstType,class SecondType>
struct cons
{
typedef FirstType first_type;
typedef SecondType second_type;
};
//有了上面的cons的实现,现在就可以很容易的将类型放入到这个串中了:
typedef cons<char,
cons<int,
cons<short,
cons<long,
cons<float,
double> > > > > CONS;
//上面的代码定义了一个6中基本类型作为元素的类型串CONS,那么该怎么实现对上面的类
//型的操作呢?仔细想想C语言里面的字符串是如何处理字符串的终结的啊!呵呵可能你已
//经想到了,需要一个特殊的终结符,对了,为了能够操作上面的类型容器我们需要定义
//一个终结符类型:
struct null_type;
//有了上面的null_type,现在的类型容器CONS重新表述如下:
typedef cons<char,
cons<int,
cons<short,
cons<long,
cons<float,
cons<double,
null_type> > > > > > CONS;
#endif//CODE_NOTE
#if 0
有了上面的cons类型和终结符已经可以用来表达类型串的概念了,既然有了类型串,
那就应该有类型串的相关操作,这里我们首先考虑的是,这些操作将会应用到什么地方?
考虑一下如何使用前面的LOOP静态循环来遍历cons串中的所有类型呢?很显然通过本文开
头对前文的总结(5)我们知道静态LOOP循环可以变化的只能是整数,那么很自然的为了能够
实现类型遍历必须实现整数和类型的映射和反映射元函数。一种直接的方式就是根据索引
值得到cons串中的每一个类型,这样就需要至少两个元函数来完成这种循环,这两个元函
数分别是:
(a)length元函数,用来获得cons串的长度
(b)type元函数,用来根据索引值得到相应的cons串在该位置的类型
有了上面的两个元函数就可以很方便的使用LOOP静态循环来实现类型遍历的过程了。
现面来看看如何实现这两个元函数:在给出这两个元函数的实现之前,为了明确起见,需
要准确定义这两个元函数的返回值的意义
(length)根据具体的CONS类型得到的是cons串的长度,这个长度指的是不包括类型串
结束符的元素个数,这一点和C字符串的strlen库函数的行为类似。
(type)根据具体的CONS类型和一个给定的索引值得到一个类型,指的是cons类型串中
的指定索引值的位置的类型,这个索引值符合C语言的规范,是从0开始编号的。
在给出具体的这两个元函数的实现之前需要将前面的章节中的命名规范说明一下:
(A)如果元函数的返回值为整数,那么返回值名称命名为value
(B)如果元函数的返回值为类型,那么返回值名称命名为result
好了,现在可以看看如何实现这两个元函数了:
#endif
#ifdef CODE_NOTE
//length元函数的实现
template<class Type>struct length;
template<>struct length<null_type>
{//返回值为整数,命名为value
enum{value=0};
};
template<class FirstType,class SecondType>
struct length<cons<FirstType,SecondType> >
{//返回值为整数,命名为value
enum{value=1+length<SecondType>::value};
};
//type元函数的实现
template<class Cons,size_t index>struct type;
template<class FirstType,class SecondType>
struct type<cons<FirstType,SecondType>,0>
{//返回值为类型,命名为result
typedef FirstType result;
};
template<class FirstType,class SecondType,size_t i>
struct type<cons<FirstType,SecondType>,i>
{//返回值为类型,命名为result
typedef typename type<SecondType,i-1>::result result;
};
#endif//CODE_NOTE
#if 0
两个需要的元函数已经成功的给出了,如果有什么不清楚的地方也可以参见<<Modern
C++ Design>>一书的Typelist的Length元函数和TypeAt元函数的解释,基本上是一样的,
这里不在进行过多的解释。在这里我们所重视的是如何使用这两个元函数来实现我们的类
型遍历算法。现咱万事具备了,可以开始本章的中心内容:实现类型的遍历算法!不过在
给出具体的代码之前还需要做一件事情,那就是将之前的代码保存到一个文件中,为了统
一起见,我们就先将这个文件名命名为meta.h吧!该文件的详细内容在本文的最后给出。
现在看一下如何实现我们的类型遍历算法吧,见CODE1所示:
#endif
#ifdef CODE1
#include <iostream>
#include "meta.h"//该文件的具体内容见本文的最后
namespace xcl = pandaxcl;//缩写名字空间
//为了能够测试type元函数需要定义下面的一些额外的测试模板
template<class T> struct traits;
template<> struct traits<char >{static const char*name(){return "char";}};
template<> struct traits<int >{static const char*name(){return "int";}};
template<> struct traits<short >{static const char*name(){return "short";}};
template<> struct traits<long >{static const char*name(){return "long";}};
template<> struct traits<float >{static const char*name(){return "float";}};
template<> struct traits<double>{static const char*name(){return "double";}};
//必须外加一层包裹层来传递额外的类型参数
template <class T> struct Config
{
template<size_t i> struct Function
{
static void execute()
{
//你的代码在这里编写
typedef typename xcl::type<T,i>::result CT;//当前类型的意思
std::cout << i << " : " << traits<CT>::name() << std::endl;
}
};
};
int main()
{
typedef xcl::cons<char,
xcl::cons<int,
xcl::cons<short,
xcl::cons<long,
xcl::cons<float,
xcl::cons<double,
xcl::null_type> > > > > > CONS;
std::cout << "CONS类型串的长度为:" << xcl::length<CONS>::value << std::endl;
xcl::LOOP<Config<CONS>::Function,0,xcl::length<CONS>::value,1>::execute();
return 0;
}
#endif//CODE1
//该程序的运行结果为:
/*******************************************************************************
CONS类型串的长度为:6
0 : char
1 : int
2 : short
3 : long
4 : float
5 : double
*******************************************************************************/
#if 0
从CODE1中的代码可以看出,我们已经成功的实现了类型的循环遍历。之所以需要实现
这种功能是因为这种功能能够在下一章中对自动生成的代码执行任意的操作,这一点在自
动生成代码上非常重要,如果这一点没有实现,C++模板元自动生成的代码和C宏生成的代
码相比将不会有什么神奇指出。这也是我将这段内容安排在这一章的主要原因。可能您还
没有认识到这里的代码的重要性,不过这没什么关系,我会在后续的章节中详细说明这里
的类型遍历的使用过程,并实现动态代码和静态代码的连接。这样之后您自然就会了解本
章的意义所在了:)
下面给出CODE1中的头文件meta.h的内容如下:
#endif
#ifdef CODE_NOTE
#pragma once
namespace pandaxcl{
//
template <bool Condition,class Then,class Else>
struct IF
{
typedef Then result;//将Then类型作为条件为真的返回值(返回值为类型)
};
template<class Then,class Else>
struct IF<false,Then,Else>
{
typedef Else result;//将Else类型作为条件为假的返回值(返回值为类型)
};
//
//
加入一个外覆层来传递额外的模板参数
template <template<size_t>class Function,size_t start,size_t finish,size_t step>
struct LOOP
{
//为了能够正确的计算出实际的循环终止变量,需要对给定的终止变量
//进行计算,以满足正确的循环语义
enum{real_finish=(finish/step*step+start)};
static void execute()
{
LOOP_BODY<real_finish,true>::execute();
}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
LOOP_BODY<real_finish,true>::execute(e);
}
private:
//引入了一个布尔型的模板参数用来确定循环的终止条件
template <size_t i,bool> struct LOOP_BODY
{
static void execute()
{
LOOP_BODY<i-step,(i-step>start)>::execute();
Function<i-step>::execute();
}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
LOOP_BODY<i-step,(i-step>start)>::execute(e);
Function<i-step>::execute(e);
}
};
//循环的终止语句,停止递归以结束循环
template <size_t i> struct LOOP_BODY<i,false>
{
static void execute(){}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e){}
};
};
//为了模板化必须将原来的输出函数做成一个模板结构体
//template<size_t n> struct Function
//{
// static void execute()
// {
// //你的代码在这里编写
// }
//};
//
//
//cons的实现,采用和STL类似的类型命名方式
template <class FirstType,class SecondType>
struct cons
{
typedef FirstType first_type;
typedef SecondType second_type;
};
struct null_type;//类型串终结符
//下面是两个为了实现静态类型循环所需要的静态元函数
//length元函数的实现
template<class Type>struct length;
template<>struct length<null_type>
{//返回值为整数,命名为value
enum{value=0};
};
template<class FirstType,class SecondType>
struct length<cons<FirstType,SecondType> >
{//返回值为整数,命名为value
enum{value=1+length<SecondType>::value};
};
//type元函数的实现
template<class Cons,size_t index>struct type;
template<class FirstType,class SecondType>
struct type<cons<FirstType,SecondType>,0>
{//返回值为类型,命名为result
typedef FirstType result;
};
template<class FirstType,class SecondType,size_t i>
struct type<cons<FirstType,SecondType>,i>
{//返回值为类型,命名为result
typedef typename type<SecondType,i-1>::result result;
};
//
}//namespace pandaxcl{
#endif//CODE_NOTE
#if 0
从上面的meta.h文件中我们还可以看出,LOOP静态循环代码里面比前一篇文章中的
LOOP循环多了一些额外的代码,这是为了能够实现静态代码和动态代码的连接的,这种连
接通常是通过函数参数的形式实现的。至于为什么需要这一点将会在下一篇文章的代码自
动生成一章中进行讨论。(敬请关注!)
本章完。
未完,待续...
#endif
#if 0
这一章,我们将要开始的讨论C++里面的代码生成技术。说起代码生成技术,实际上这
并不是C++的专利,作为C++子集的C语言早就已经使用了一定的代码生成技术,这就是C宏
。我想C宏大家应该非常熟悉了吧,特别是能够实现带参数的宏使得大量的库利用这种技术
来生成那些格式重复的代码,例如:微软的MFC库,跨平台的GUI库wxWidget,Boost库等等
都使用了C宏。虽然C宏在这些库里面扮演了非常重要的角色,并且仍将扮演非常重要的角
色,但是也不得不说:C宏存在着很大的问题。最基础的就是类型不安全性,这也是C++里
面出现模板语素的一个比较重要的原因。更重要的是使用C宏生成的代码仅仅只是实现了简
单的格式填空能力,并不能表达特定的算法。正是C宏的表达设计思想的不足限制了C宏的
使用范围。
说起C++模板的代码生成能力,说起来这也是一种巧合,自从90年代初期第一个C++模
板元程序(用来在编译期输出质数)被发现以来,C++迷们对模板元程序的研究就热闹起来
了,并出现了大量的关于C++模板元程序的文献。在这里我所介绍的模板元代码生成技术主
要参考了<<Modern C++ Design>>一书的GenScatterHierarchy结构,并对这种结构进行了
扩展应用,采用了前面的LOOP静态循环实现对这种结构生成的代码的操作,从而完成了一
个C++普通类的自动生成过程。所谓的C++普通类指的是一般的手工直接编写的一个类,这
种类通常包含成员变量,生成成员变量的过程可以由GenScatterHierarchy结构完成,但是
仅仅有了成员变量还不能成为一个C++类,或许成为结构体更合适;另外普通类一般还包含
了成员函数,这种成员函数的自动生成就不能通过Loki库来实现自动生成了,虽然Loki库
的GenLinearHierarchy结构可以生成函数接口,但是函数体里面的内容就不能够随心所欲
的编写了。这后面的一点正是在本文中将要进行详细讨论的。
好了,现在我们来分析前面的章节中介绍的模板元技术中已经蕴涵的代码生成技术。
实际上LOOP静态循环中已经实现了静态函数的自动生成,也就是说,编译器在编译的时候
确确实实是看到了循环所产生的所有的静态函数,而并不是运行的时候进行的函数递归调用
。下面我们来看看C++里面的多继承现象和参数化继承现象:
#endif
#ifdef CODE_NOTE
//多继承现象
class Base1{};
class Base2{};
class Base3{};
class Derived:public Base1,public Base2,public Base3{};
//模板参数化继承现象:
template <class T>Base{};
class Derived:public Base<char>,public Base<int>,public Base<float>{};
#endif//CODE_NOTE
#if 0
从上面的多继承和参数化的多继承我们可以得到什么灵感呢?如果没有,那么再考虑
一下上一章中所介绍的类型串类型,^_^这时候有没有灵感了呢?呵呵,实际上上面的代码
中的参数化多继承的基类就是一个类型遍历过程,针对每一个类型,用Base包裹住每一个
类型并作为Derived类的基类,这样就可以生成一个自己定制的类了。如果能够使这个过程
自动化,那么我们就可以认为代码被自动生成了。
现在考虑一下上面的自动化过程所需要的输入和输出分别是什么:
输入:一个cons类型串记录所有的需要的类型,一个包裹模板类
输出:生成一个由所有的cons类型串中的类型作为模板参数的包裹类作为基类的类
这样如果在包裹类里面定义了一个模板参数类型的成员变量,那么生成的类中就有所
有的这些类型的变量,也就是说这些变量都成了生成的类的成员变量。
好了,说到这里,我们来看看具体的实现过程是怎样的:
#endif
#ifdef CODE_NOTE
//下面是实现代码自动生成的模板元函数,主要参考了Loki的代码
//为了撤销和重做库的独立性,将该功能从Loki库中提取出来
template<class T,template<class>class Unit>
struct scatter : public Unit<T>
{
};
template<class H,class T,template<class>class Unit>
struct scatter<cons<H,T>,Unit>
: public scatter<H,Unit>
, public scatter<T,Unit>
{
typedef cons<H,T> cons_type;
};
//下面的null_type参看前一章中的代码
template<template<class>class Unit>
struct scatter<null_type,Unit>
{
};
#endif//CODE_NOTE
#if 0
在给出具体的测试代码之前还需要做一件事情,那就是将上面的scatter代码放到meta.h文件中
便于代码组织,也是为了使用前面的类型null_type。关于本文所使用的meta.h文件仅仅只是在前一
章的meta.h文件的末尾追加了上面的scatter元函数,详细的内容在本文的最后给出。下面看看如何
使用上面的模板元函数scatter来自动生成代码,见CODE1:
#endif
#ifdef CODE1
//下面的是测试代码
#include <iostream>
#include "meta.h"//这里的meta.h文件内容比上一章的内容多了一些,见本文末尾
namespace xcl=pandaxcl;
template <class T> struct Unit
{
T _value;//成员变量
};
template <class Cons>
class Class:public xcl::scatter<Cons,Unit>
{//注意这里没有任何的成员函数
};
int main()
{
typedef xcl::cons<char,
xcl::cons<int,
xcl::cons<short,
xcl::cons<long,
xcl::cons<float,
xcl::cons<double,
xcl::null_type> > > > > >CONS;
Class<CONS> var;//声明一个类型变量
std::cout << sizeof(var) << std::endl;
std::cout << sizeof(Class<CONS>) << std::endl;
//下面的这些代码的成功编译标志着上面的过程确确实实产生了代码
static_cast<Unit<char >&>(var)._value = 1;
static_cast<Unit<int >&>(var)._value = 1;
static_cast<Unit<short >&>(var)._value = 1;
static_cast<Unit<long >&>(var)._value = 1;
static_cast<Unit<float >&>(var)._value = 1;
static_cast<Unit<double>&>(var)._value = 1;
return 0;
}
#endif//CODE1
//该程序的运行结果如下:
/*******************************************************************************
48
48
*******************************************************************************/
#if 0
从上面的程序中我们可以看出代码生成过程确实是成功完成了,但是我们还应该注意
到上面的类型串中的的类型是绝对不允许重复的,否则后面的static_cast静态转型将会出
现模棱两可的情况。这一点在Loki库已经成功的解决了,但是在我们将要讨论的撤销和重
做库中并不会出现这种情况,所以为了使得代码尽可能的简单,我们就采用最简单的方式
。因为简单的就是最好的嘛!实际上通过这种简单的类型串规定再通过外覆一个包裹层同
样可以解决类型重复的问题。
上面的生成的类中已经具备了成员变量,但这仅仅相当于实现了一种结构体,离真正
的使用还有一段距离。因为生成的类中没有成员函数,只有在有了成员函数之后才可以真
正的实用化,这就是前面的章节中介绍的cons类型和静态LOOP循环的联合使用发挥威力的
时候了。好,首先让我们来看一个具体的问题:
考虑下面的自动生成的类:
#endif
#ifdef CODE_NOTE
template<class T> struct Unit:public std::vector<T>{};
template<class Cons> class Class : public xcl::scatter<Cons,Unit>
{
public:
//仔细考虑一下下面的这个成员函数应该如何实现?
//该成员函数就是为了输出自动生成的类中的所有的成员变量
//(std::vector容器成员变量)的内容。
void print(){}
};
#endif//CODE_NOTE
#if 0
从上面自动生成的类来说,为了能够自动的根据类型将所有的成员变量的内容都
进行输出,需要写一个print成员函数,这个成员函数能够根据类型串的不同而自动的
生成相应的处理代码,关于这一点的实现参见代码CODE2:
#endif
#ifdef CODE2
#include <iostream>
#include <iterator>
#include <vector>
#include <complex>
#include <string>
#include "meta.h"//见本文末尾
namespace xcl=pandaxcl;
template<class T> struct Unit:public std::vector<T>{};
template<class Cons> class Class : public xcl::scatter<Cons,Unit>
{
template<size_t i> struct PRINT
{
template<class EnvironmentType>
static void execute(EnvironmentType&e)
{
//你的代码在这里编写
typedef typename xcl::type<Cons,i>::result CT;
Unit<CT> &v = static_cast<Unit<CT>&>(e);
std::copy(v.begin(),v.end(),std::ostream_iterator<CT>(std::cout," "));
std::cout << std::endl;//用来分开不同类型的数据
}
};
public:
//下面是成员函数得实现
void print()
{
//通过参数传递实现了静态代码和动态代码的连接
xcl::LOOP<PRINT,0,xcl::length<Cons>::value,1>::execute(*this);
}
};
int main()
{
{//这是一个自动生成类的测试
typedef xcl::cons<char,
xcl::cons<int,
xcl::cons<float,
xcl::null_type> > > CONS;
Class<CONS> var;
//在输出之前需要初始化var变量
static_cast<Unit<char>&>(var).push_back('A');
static_cast<Unit<char>&>(var).push_back('B');
static_cast<Unit<char>&>(var).push_back('C');
static_cast<Unit<int>&>(var).push_back(1);
static_cast<Unit<int>&>(var).push_back(2);
static_cast<Unit<int>&>(var).push_back(3);
static_cast<Unit<int>&>(var).push_back(4);
static_cast<Unit<float>&>(var).push_back(1.1);
static_cast<Unit<float>&>(var).push_back(2.2);
static_cast<Unit<float>&>(var).push_back(3.3);
static_cast<Unit<float>&>(var).push_back(4.4);
//输出所有的成员变量的内容
var.print();
}
{//这是另一个自动生成类的测试
//修改了类型串之后不需要修改原来的print成员就可以实现所有的输出
typedef std::complex<float> COMPLEX;
typedef xcl::cons<char,
xcl::cons<COMPLEX,
xcl::cons<std::string,
xcl::null_type> > > CONS;
Class<CONS> var;
//在输出之前需要初始化var变量
static_cast<Unit<char>&>(var).push_back('A');
static_cast<Unit<char>&>(var).push_back('B');
static_cast<Unit<char>&>(var).push_back('C');
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(1.1,0.1));
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(2.2,0.2));
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(3.3,0.3));
static_cast<Unit<COMPLEX>&>(var).push_back(COMPLEX(4.4,0.4));
static_cast<Unit<std::string>&>(var).push_back("熊春雷");
static_cast<Unit<std::string>&>(var).push_back("熊猫");
static_cast<Unit<std::string>&>(var).push_back("国宝");
static_cast<Unit<std::string>&>(var).push_back("开心");
static_cast<Unit<std::string>&>(var).push_back("pandaxcl");
//输出所有的成员变量的内容
var.print();
}
return 0;
}
#endif//CODE2
//该程序的运行结果如下:
/*******************************************************************************
A B C
1 2 3 4
1.1 2.2 3.3 4.4
A B C
(1.1,0.1) (2.2,0.2) (3.3,0.3) (4.4,0.4)
熊春雷 熊猫 国宝 开心 pandaxcl
*******************************************************************************/
#if 0
从CODE2中可以看出,自动化的类Class可以根据传递的类型串自动的调整成员变量数
量,同时也会自动调整print成员函数的功能。其中后者是本文所介绍的方法,也是模板元
函数用来进行自动化编程的关键。通过print成员函数的演示,这里你是否有很多的想法呢
?呵呵:),是啊既然可以让成员函数也能够实现自动化,那么采用C++模板元编写自动化的
类将是一件可行的事情,这就看各位的发挥了。
抛砖引玉的过程基本上已经完备,接下来就是介绍一下为什么需要自动化编程的背景
了。我们在编写程序代码的时候通常是将各种不同的功能分成一个个的小模块,当一个个
的模块编写好了之后就可以以库的形式提供给客户端使用,同时还需要附带一个非常详细
的文档,用来说明如何使用这个库。通常来说,每一个库的文档都是不同的,都有各自不
同的特殊要求,这就要客户端非常熟悉所使用的库,但是通常来说,库的编写者非常熟悉
自己编写的库,而库的使用者却不熟悉这个库。在这里介绍的自动化编程就是将这种矛盾
进行缓解的一种努力方式。库的编写者将库的使用规则尽可能的编码到库中,留给库的使
用者一个通用而简单的使用界面,将那些特殊的规则留给库的编写者而不是库的使用者不
仅可以降低库的使用门槛,同时还可以大大减少使用库的过程中发生错误的可能性。
以上是我的观点,仅供参考。
本章完。
从本文还可以看出,文中实际上还留下了一个问题:自动生成代码使用的类型串中不
允许有重复类型出现,这一点实际上是一个缺陷,将会在下一章中解决。但是作为原理来
讲,本章介绍的方法在下一章里面用来解决重复类型的类型串生成代码的时候也需要采用
了这里的方法,所以非常有必要先在这里介绍这种方法。在下一章里面将会讨论如何使用
本章介绍的不含重复类型的类型串生成代码的技术用来解决含有重复类型的类型串生成代
码的问题。注意下一章中采用的方法和Loki库中采用的方法可不相同哦:)(敬请关注!)
未完。待续...
#endif
#ifdef CODE_NOTE//附录:本文使用的meta.h文件内容
#pragma once
namespace pandaxcl{
//
template <bool Condition,class Then,class Else>
struct IF
{
typedef Then result;//将Then类型作为条件为真的返回值(返回值为类型)
};
template<class Then,class Else>
struct IF<false,Then,Else>
{
typedef Else result;//将Else类型作为条件为假的返回值(返回值为类型)
};
//
//
加入一个外覆层来传递额外的模板参数
template <template<size_t>class Function,size_t start,size_t finish,size_t step>
struct LOOP
{
//为了能够正确的计算出实际的循环终止变量,需要对给定的终止变量
//进行计算,以满足正确的循环语义
enum{real_finish=(finish/step*step+start)};
static void execute()
{
LOOP_BODY<real_finish,true>::execute();
}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
LOOP_BODY<real_finish,true>::execute(e);
}
private:
//引入了一个布尔型的模板参数用来确定循环的终止条件
template <size_t i,bool> struct LOOP_BODY
{
static void execute()
{
LOOP_BODY<i-step,(i-step>start)>::execute();
Function<i-step>::execute();
}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
LOOP_BODY<i-step,(i-step>start)>::execute(e);
Function<i-step>::execute(e);
}
};
//循环的终止语句,停止递归以结束循环
template <size_t i> struct LOOP_BODY<i,false>
{
static void execute(){}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e){}
};
};
//为了模板化必须将原来的输出函数做成一个模板结构体
//template<size_t i> struct Function
//{
// static void execute()
// {
// //你的代码在这里编写
// }
//};
//
//
//cons的实现,采用和STL类似的类型命名方式
template <class FirstType,class SecondType>
struct cons
{
typedef FirstType first_type;
typedef SecondType second_type;
};
struct null_type;//类型串终结符
//下面是两个为了实现静态类型循环所需要的静态元函数
//length元函数的实现
template<class Type>struct length;
template<>struct length<null_type>
{//返回值为整数,命名为value
enum{value=0};
};
template<class FirstType,class SecondType>
struct length<cons<FirstType,SecondType> >
{//返回值为整数,命名为value
enum{value=1+length<SecondType>::value};
};
//type元函数的实现
template<class Cons,size_t index>struct type;
template<class FirstType,class SecondType>
struct type<cons<FirstType,SecondType>,0>
{//返回值为类型,命名为result
typedef FirstType result;
};
template<class FirstType,class SecondType,size_t i>
struct type<cons<FirstType,SecondType>,i>
{//返回值为类型,命名为result
typedef typename type<SecondType,i-1>::result result;
};
//
//下面是实现代码自动生成的模板元函数,主要参考了Loki的代码
//为了撤销和重做库的独立性,将该功能从Loki库中提取出来
template<class T,template<class>class Unit>
struct scatter : public Unit<T>
{
};
template<class H,class T,template<class>class Unit>
struct scatter<cons<H,T>,Unit>
: public scatter<H,Unit>
, public scatter<T,Unit>
{
typedef cons<H,T> cons_type;
};
//下面的null_type参看前一章中的代码
template<template<class>class Unit>
struct scatter<null_type,Unit>
{
};
//
}//namespace pandaxcl{
#endif//CODE_NOTE//附录
#if 0
在上一篇文章里面讨论了C++里面的代码生成技术,使用的是scatter,不过上一篇文
章里面也提到了,前一篇文章里面讨论的代码生成模板scatter使用的类型串绝对不允许
重复。其实上一篇中的scatter使用由重复的类型的类型串也是能够正常生成代码的,不
过产生的代码却不能将类型重复的变量分辨出来,这样生成的代码就没有了什么实际意义
,所以在这一章中将要解决的问题是:重新编写一个可以使用重复类型的类型串生成代码
,并且能够采用一定的方法将这些生成的变量分辨出来。
那么该如何编写这里需要的代码呢?上一章里面的scatter见下面的代码:
#endif
#ifdef CODE_NOTE
template<class T,template<class>class Unit>
struct scatter : public Unit<T>
{
};
template<class H,class T,template<class>class Unit>
struct scatter<cons<H,T>,Unit>
: public scatter<H,Unit>
, public scatter<T,Unit>
{
typedef cons<H,T> cons_type;
};
template<template<class>class Unit>
struct scatter<null_type,Unit>
{
};
#endif//CODE_NOTE
#if 0
虽然上面的代码生成机制生成的重复类型的代码不能够通过静态转型(static_cast)分
辨出来,但是上面的代码却给出另外一种重要的思想:使用不含有重复类型的类型串采用
上面的代码生成技术生成的代码却可以使用静态转型(static_cast)分辨出来。前一章里面
的例子都采用了这种思想。那么现在我们可不可以同样使用这种思想编写出一个可以使用
重复类型的类型串生成代码,并且能够通过静态转型(static_cast)方法将这些生成的变量
分辨出来呢?答案是肯定的,在这里同样是采用前面所讨论的多加一层包裹的方法来实现。
我们知道对于下面的模板类:
#endif
#ifdef CODE_NOTE
template <size_t>
struct Struct
{
};
Struct<0> a;
Struct<1> b;
Struct<2> c;
Struct<3> d;
//... ...
#endif//CODE_NOTE
#if 0
上面的Struct模板的模板参数不同将会生成不同的类型。例如:上面的Struct<0>、
Struct<1>、Struct<2>和Struct<3>是四种不同的类型。这一点就像是给这个模板编号让这
个编号的不同来区分出不同的类型。正是因为这种现象的存在我们才有了这样的思想:在
新的scatter代码生成机制中给每一个串中的类型声明变量的时候额外添加一个类型索引号
。这样就可以使用静态转型方法分辨出同类型的变量了。见代码CODE1所示:
#endif
#ifdef CODE1
#include <iostream>
#include "meta.h"//为了引入pandaxcl::cons
//下面的代码仅仅是为了测试产生代码的索引号和类型是否匹配而引入的特性类模板
//说得简单点,这里所谓的特性类就是一种大型的switch,case结构,只不过是跟据
//类型的不同由编译器来自动选择相应的模板。在这里讨论的特性类实际上可以应用
//到撤销和重做框架的序列化方案中去,当然这是为了保存类型名称了。
template <class T> struct traits;
template <> struct traits<char>{static const char*name(){return "char";}};
template <> struct traits<int >{static const char*name(){return "int ";}};
//下面的这个产生代码的格式还可以使用索引号得到基元对象注意了:下面的递归模板的
//终结条件并不是第一个模板参数size_t而是第二个模板参数T,这一点很重要,因为前面
//的文章中讨论的都是以数字0作为终结条件的。所以这里需要特别说明一下,另外也可以
//给你一点灵感:)
template<size_t i,class T,template<class>class Unit>
struct scatter_helper : public Unit<T>
{
//下面的构造函数仅仅是为了输出索引号对应的类型信息而引入的
scatter_helper()
{
std::cout << traits<T>::name() << " : " << i << std::endl;
}
};
template<size_t i,class H,class T,template<class>class Unit>
struct scatter_helper<i,pandaxcl::cons<H,T>,Unit>
: public scatter_helper<i,H,Unit>
, public scatter_helper<i+1,T,Unit>//递增的为每一个类型添加一个额外的索引号
{
typedef pandaxcl::cons<H,T> cons_type;
};
//下面的null_type参看前一章中的代码
template<size_t i,template<class>class Unit>
struct scatter_helper<i,pandaxcl::null_type,Unit>
{
};
//为了和前面章节中讨论的scatter兼容也是为了方便使用,这里重新对scatter_helper进行
//包装生成了一个模板类scatter,这样前面使用的任何方法针对于这个新的scatter类同样
//适用。
template <class Cons,template<class>class Unit>
struct scatter:public scatter_helper<0,Cons,Unit>
{
typedef Cons cons_type;
};
//下面的函数用来根据类型索引号来得到相应的基元类型
template <size_t i,class Cons,template<class>class Unit>
scatter_helper<i,typename pandaxcl::type<Cons,i>::result,Unit>&
field(scatter<Cons,Unit>&obj)
{
typedef typename pandaxcl::type<Cons,i>::result CT;
typedef scatter_helper<i,CT,Unit> RT;
return static_cast<RT&>(obj);
};
template <size_t i,class Cons,template<class>class Unit>
const scatter_helper<i,typename pandaxcl::type<Cons,i>::result,Unit>&
field(const scatter<Cons,Unit>&obj)
{
typedef typename pandaxcl::type<Cons,i>::result CT;
typedef scatter_helper<i,CT,Unit> RT;
return static_cast<const RT&>(obj);
};
//
//下面是测试代码
namespace xcl=pandaxcl;
template <class T> struct TestUnit
{
T _value;
};
int main()
{
typedef xcl::cons<int,
xcl::cons<char,
xcl::cons<char,
xcl::cons<int,
xcl::null_type> > > >CONS;
std::cout << "========scatter_helper========" <<std::endl;
{
scatter_helper<0,CONS,TestUnit> v;
static_cast<scatter_helper<0,int ,TestUnit>&>(v)._value = 10;
static_cast<scatter_helper<1,char,TestUnit>&>(v)._value = 'B';
static_cast<scatter_helper<2,char,TestUnit>&>(v)._value = 'C';
static_cast<scatter_helper<3,int ,TestUnit>&>(v)._value = 11;
std::cout << static_cast<scatter_helper<0,int ,TestUnit>&>(v)._value << " ";
std::cout << static_cast<scatter_helper<1,char,TestUnit>&>(v)._value << " ";
std::cout << static_cast<scatter_helper<2,char,TestUnit>&>(v)._value << " ";
std::cout << static_cast<scatter_helper<3,int ,TestUnit>&>(v)._value << " ";
}
std::cout << std::endl << "========field(scatter&)=======" <<std::endl;
{
scatter<CONS,TestUnit> v;
field<0>(v)._value = 20;
field<1>(v)._value = 'F';
field<2>(v)._value = 'G';
field<3>(v)._value = 21;
std::cout << field<0>(v)._value << " ";
std::cout << field<1>(v)._value << " ";
std::cout << field<2>(v)._value << " ";
std::cout << field<3>(v)._value << " ";
}
std::cout << std::endl << "=====field(const scatter&)====" <<std::endl;
{
scatter<CONS,TestUnit> v;
field<0>(v)._value = 30;
field<1>(v)._value = 'J';
field<2>(v)._value = 'K';
field<3>(v)._value = 31;
//测试常量情况下的field辅助函数
const scatter<CONS,TestUnit>&cv = v;
std::cout << field<0>(cv)._value << " ";
std::cout << field<1>(cv)._value << " ";
std::cout << field<2>(cv)._value << " ";
std::cout << field<3>(cv)._value << " ";
}
std::cout << std::endl << "==============================" <<std::endl;
return 0;
}
#endif//CODE1
//该程序运行结果如下:
/*******************************************************************************
========scatter_helper========
int : 0
char : 1
char : 2
int : 3
10 B C 11
========field(scatter&)=======
int : 0
char : 1
char : 2
int : 3
20 F G 21
=====field(const scatter&)====
int : 0
char : 1
char : 2
int : 3
30 J K 31
==============================
*******************************************************************************/
#if 0
从运行结果可以看出我们已经成功的解决了本文开头所提出的问题。这个新的代码产生
模板已经写出来了。为了以后使用的方便需要将这个模板类加入到meta.h中。不过本文中使
用的meta.h并没有包含这里的scatter模板类,见附录所示。
本章完。
实际上C++模板元编程的基础与应用中的基础篇已经写完了,从下一章开始将会讨论这种
编程技术的一些应用专题。下一章里面将会涉及到的专题是类型特性trait。(敬请关注!)
未完,待续...
#endif
#ifdef CODE_NOTE//附录:本文采用的meta.h文件内容如下:
#pragma once
namespace pandaxcl{
//
template <bool Condition,class Then,class Else>
struct IF
{
typedef Then result;//将Then类型作为条件为真的返回值(返回值为类型)
};
template<class Then,class Else>
struct IF<false,Then,Else>
{
typedef Else result;//将Else类型作为条件为假的返回值(返回值为类型)
};
//
//
加入一个外覆层来传递额外的模板参数
template <template<size_t>class Function,size_t start,size_t finish,size_t step>
struct LOOP
{
//为了能够正确的计算出实际的循环终止变量,需要对给定的终止变量
//进行计算,以满足正确的循环语义
enum{real_finish=(finish/step*step+start)};
static void execute()
{
LOOP_BODY<real_finish,true>::execute();
}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
LOOP_BODY<real_finish,true>::execute(e);
}
private:
//引入了一个布尔型的模板参数用来确定循环的终止条件
template <size_t i,bool> struct LOOP_BODY
{
static void execute()
{
LOOP_BODY<i-step,(i-step>start)>::execute();
Function<i-step>::execute();
}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
LOOP_BODY<i-step,(i-step>start)>::execute(e);
Function<i-step>::execute(e);
}
};
//循环的终止语句,停止递归以结束循环
template <size_t i> struct LOOP_BODY<i,false>
{
static void execute(){}
//下面的这个模板函数是为了能够实现静态代码和动态代码连接
template <class EnvironmentType>
static void execute(EnvironmentType&e){}
};
};
//为了模板化必须将原来的输出函数做成一个模板结构体
//template<size_t i> struct Function
//{
// static void execute()
// {
// //你的代码在这里编写
// }
//};
//
//
//cons的实现,采用和STL类似的类型命名方式
template <class FirstType,class SecondType>
struct cons
{
typedef FirstType first_type;
typedef SecondType second_type;
};
struct null_type;//类型串终结符
//下面是两个为了实现静态类型循环所需要的静态元函数
//length元函数的实现
template<class Type>struct length;
template<>struct length<null_type>
{//返回值为整数,命名为value
enum{value=0};
};
template<class FirstType,class SecondType>
struct length<cons<FirstType,SecondType> >
{//返回值为整数,命名为value
enum{value=1+length<SecondType>::value};
};
//type元函数的实现
template<class Cons,size_t index>struct type;
template<class FirstType,class SecondType>
struct type<cons<FirstType,SecondType>,0>
{//返回值为类型,命名为result
typedef FirstType result;
};
template<class FirstType,class SecondType,size_t i>
struct type<cons<FirstType,SecondType>,i>
{//返回值为类型,命名为result
typedef typename type<SecondType,i-1>::result result;
};
//index元函数根据类型得到类型串中该类型位置
template<class Cons,class Test>struct index;
template<class Test> struct index<null_type,Test>
{
enum{value=-1};
};
template<class T,class Test> struct index<cons<Test,T>,Test>
{
enum{value=0};
};
template<class H,class T,class Test> struct index<cons<H,T>,Test>
{
private:
enum{temp=index<T,Test>::value};
public:
enum{value=(temp==-1?-1:1+temp)};
};
//
//下面是实现代码自动生成的模板元函数,主要参考了Loki的代码
//为了撤销和重做库的独立性,将该功能从Loki库中提取出来
template<class T,template<class>class Unit>
struct scatter : public Unit<T>
{
};
template<class H,class T,template<class>class Unit>
struct scatter<cons<H,T>,Unit>
: public scatter<H,Unit>
, public scatter<T,Unit>
{
typedef cons<H,T> cons_type;
};
//下面的null_type参看前一章中的代码
template<template<class>class Unit>
struct scatter<null_type,Unit>
{
};
//
}//namespace pandaxcl{
#enidf//CODE_NOTE