当前位置:Linux教程 - Linux综合 - C++自动化(模板元)编程基础与应用

C++自动化(模板元)编程基础与应用

#if 0   大家好,在这一类的文章中将会系统的介绍模板元编程的相关基础知识。最主要的是这类文章的目的就是让不熟悉模板元的C++迷们尽快的进入到模板元的奇妙世界里面来,所以每一篇文章都将只会讨论一个话题,尽可能的把这个话题说清楚明白就可以了。    好了,言归正传。大家都知道C++是一们计算机语言,这一点也没错,但是你知道C++里面还包含了另外一种子语言么?呵呵,恐怕知道的人就不多了,会用的人就更少了。但是既然经过了这么多年的发展,C++语言里面出现了这种子语言(模板)自然有它的根源的,这一点并不是本文中将要讨论的,如果想了解C++的发展例程,可以参见相关的文献。    在本文中将要说明的问题是:为什么说C++模板是一种语言呢?    为了回答这个问题,首先需要考虑一下什么是计算机语言,关于这个精确的定义,很多的计算机基础教程上都有,在这里我给出一种比较窄的定义:    能够在计算机上表达选择结构,循环结构,同时能够进行进行整数的四则运算的体系就是一种计算机语言。    很显然,C++自然是一种计算机语言了,还有Basic,Fortran,Pascal等等都是计算机语言。之所以讨论这么多的概念问题是为了说明:如何证明C++的模板语法是一种计算机语言。又因为模板是C++语言的一个元素,所以又可以将C++模板语法称为C++的二级语言或者子语言。在本文中将会通过使用模板分别实现整数四则运算,选择结构以及循环结构来证明C++模板语法构成了一个完整的计算机语言。    另外特别值得注意的是,因为C++的模板语言是在编译器编译的时候完成的,所以又称为静态语言,通常的C++语言又称为动态语言或者运行时语言。正是因为模板语言是在编译期完成的,所以我们可以借助于这种编译期的计算实现代码自动生成的目的,从而实现C++自动化编程。这是后续的文章中会详细讨论的。    首先看看,模板是如何完成编译期四则计算的。  #endif#ifdef CODE1//编译期四则计算的示例代码#include template strUCt Add { enum{value = i+j}; };template struct Sub { enum{value = i-j}; };template struct Mul { enum{value = i*j}; };template 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=64-2=24*2=84/2=26282*******************************************************************************/////////////////////////////////////////////////////////////////////////////////#if 0    从代码CODE1中可以看出使用整型模板参数的模板是可以实现编译期计算的,在这里,证明了这个计算过程是在编译期完成的。    好了,现在看看如何使用C++模板实现选择结构,见代码CODE2:#endif#ifdef CODE2//编译期实现选择的示例代码#include template struct IF{        typedef Then result;//将Then类型作为条件为真的返回值(返回值为类型)};templatestruct IF{        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 //为了简单采用一个阶乘作为例子,因为如果用普通的C++语法来实现阶乘函数的话需要//一个循环结构的,这里采用模板递归的方式实现了这种阶乘,也就实现了一种特殊的//循环结构。template struct Power {        enum{value=n*Power::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 template void print(){//这里的n是编译期的结果,可以用来定义数组的        int a[n+1];//这么做是为了证明n是编译期常量,同时避免出现零个元素的数组        std::cout << sizeof(a)/sizeof(int)-1 << " " ;}template struct LOOP{        static void execute(){LOOP::execute();print();}};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循环实现了静态代码和动态代码的连接,因此用途更加广泛,主要可以用来产生代码。关于如何产生代码,以及如何使用将是本类文章的后续文章讨论的内容。


[1] [2] [3] [4] [5] [6] [7] 下一页 

 

#if 0    在上一篇文章的最后提到了一个相对来说通用一点的LOOP循环,下面还是将上一篇文章中的LOOP循环代码复制如下:#endif#ifdef CODE1#include template void print(){//这里的n是编译期的结果,可以用来定义数组的        int a[n+1];//这么做是为了证明n是编译期常量        std::cout << sizeof(a)/sizeof(int)-1 << " " ;}template struct LOOP{        static void execute(){LOOP::execute();print();}};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 //下面的两个模板就比CODE1里面的LOOP模板通用多了template  

#if 0    在前面的两章里面讨论了C++模板元作为C++的一门二级语言的问题,并给出了常用的程序设计语言的语素的实现,是一个完备的体系。总的来说,前面的章节里面是采用了下面的方法来实现这些语素的:    (1)整数计算结果通过enum变量进行保存    (2)类型计算结果通过typedef进行保存    (3)?:运算符可用来实现静态整型表达式的选择功能    (4)模板特化可用来实现静态类型表达式的选择功能    (5)模板递归可用来实现静态循环,循环变化元素只能够是整数    (6)通过整数可以映射到类型,所以循环变化元素也可以间接为类型    这一章里面我们将要讨论另外的问题,所采用的方法也是这些方法。那么本文将要讨论的问题是:    如何实现类型循环,也就是上面总结出来的第(6)种技巧。    关于这一点的讨论,我认真参考了<>一书的Typelist,在本文中将会以cons来表达类型列表的概念,并对<>一书的Typelist相关的操作进行精简,得到我们将会在生成代码的过程中使用的模板元函数。不用的根本不会考虑,所以为了使撤销和重做库尽可能的独立些,所以我不采用Loki库,这样使得该撤销和重做库的安装比较简单。    为此,首先实现一个类型串类型名叫cons,代码如下:    #endif#ifdef CODE_NOTE//cons的实现,采用和STL类似的类型命名方式template struct cons{        typedef FirstType  first_type;        typedef SecondType second_type;};//有了上面的cons的实现,现在就可以很容易的将类型放入到这个串中了:typedef cons  

#if 0 这一章,我们将要开始的讨论C++里面的代码生成技术。说起代码生成技术,实际上这并不是C++的专利,作为C++子集的C语言早就已经使用了一定的代码生成技术,这就是C宏。我想C宏大家应该非常熟悉了吧,特别是能够实现带参数的宏使得大量的库利用这种技术来生成那些格式重复的代码,例如:微软的MFC库,跨平台的GUI库wxWidget,Boost库等等都使用了C宏。虽然C宏在这些库里面扮演了非常重要的角色,并且仍将扮演非常重要的角色,但是也不得不说:C宏存在着很大的问题。最基础的就是类型不安全性,这也是C++里面出现模板语素的一个比较重要的原因。更重要的是使用C宏生成的代码仅仅只是实现了简单的格式填空能力,并不能表达特定的算法。正是C宏的表达设计思想的不足限制了C宏的使用范围。 说起C++模板的代码生成能力,说起来这也是一种巧合,自从90年代初期第一个C++模板元程序(用来在编译期输出质数)被发现以来,C++迷们对模板元程序的研究就热闹起来了,并出现了大量的关于C++模板元程序的文献。在这里我所介绍的模板元代码生成技术主要参考了<>一书的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 Base{};class Derived:public Base,public Base,public Base{};#endif//CODE_NOTE#if 0 从上面的多继承和参数化的多继承我们可以得到什么灵感呢?如果没有,那么再考虑一下上一章中所介绍的类型串类型,^_^这时候有没有灵感了呢?呵呵,实际上上面的代码中的参数化多继承的基类就是一个类型遍历过程,针对每一个类型,用Base包裹住每一个类型并作为Derived类的基类,这样就可以生成一个自己定制的类了。如果能够使这个过程自动化,那么我们就可以认为代码被自动生成了。 现在考虑一下上面的自动化过程所需要的输入和输出分别是什么: 输入:一个cons类型串记录所有的需要的类型,一个包裹模板类 输出:生成一个由所有的cons类型串中的类型作为模板参数的包裹类作为基类的类 这样如果在包裹类里面定义了一个模板参数类型的成员变量,那么生成的类中就有所有的这些类型的变量,也就是说这些变量都成了生成的类的成员变量。 好了,说到这里,我们来看看具体的实现过程是怎样的:#endif#ifdef CODE_NOTE//下面是实现代码自动生成的模板元函数,主要参考了Loki的代码//为了撤销和重做库的独立性,将该功能从Loki库中提取出来template

 

#if 0 在上一篇文章里面讨论了C++里面的代码生成技术,使用的是scatter,不过上一篇文章里面也提到了,前一篇文章里面讨论的代码生成模板scatter使用的类型串绝对不允许重复。其实上一篇中的scatter使用由重复的类型的类型串也是能够正常生成代码的,不过产生的代码却不能将类型重复的变量分辨出来,这样生成的代码就没有了什么实际意义,所以在这一章中将要解决的问题是:重新编写一个可以使用重复类型的类型串生成代码,并且能够采用一定的方法将这些生成的变量分辨出来。 那么该如何编写这里需要的代码呢?上一章里面的scatter见下面的代码:#endif#ifdef CODE_NOTEtemplate

 

#if 0 在上一章里面讨论了代码的自动生成机制,经常会遇到根据不同的类型选择不同的操作的情况,也就是静态分派的问题。这就需要有一种机制用来识别不同的类型,在本章里面将会系统的讨论C++里面可用的类型识别问题。 最常见的有下面几种: (1)根据模板参数需要精确匹配类型来识别类型 (2)根据隐式自动转型来判断类型是否可以自动转型,从而可以判断是某个基类的派生类。 (3)给每一个类型都追加一个额外的模板参数用来表示不同的类型,一般都是用不同的数字映射为不同的类型来实现类型识别的,前面讨论的可以有重复类型的scatter代码产生器就采用了这种方法来识别不同的类型的。 在这一章里面将会分别讨论上面的三种情况的一般应用: #endif#ifdef CODE1//g++ -DCODE1 thisfile.cpp//采用方法(1)来识别不同的类型#include #include #include template struct traits;template <> struct traits {static const char*name(){return "char ";}};template <> struct traits {static const char*name(){return "int ";}};template <> struct traits {static const char*name(){return "short ";}};template <> struct traits {static const char*name(){return "long ";}};template <> struct traits {static const char*name(){return "float ";}};template <> struct traits {static const char*name(){return "double";}};template <> struct traits{ static const char*name(){return "std::string";}};template <> struct traits

(出处:http://www.sheup.com)


上一页 [1] [2] [3] [4] [5] [6] [7]