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

#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//附录 
————————————————
版权声明:本文为CSDN博主「pandaxcl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pandaxcl/article/details/667645

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值