企业即时通讯 - Enterprise Instant Messenger

局域网聊天工具,文字讯息、文件发送、语音通讯、高清视频通讯、远程桌面控制。

用户操作
[即时聊天] [发私信] [加为好友]
FreeEIM StudioID:i_like_cpp
972454次访问,排名32,好友4人,关注者7人。
i_like_cpp的文章
原创 888 篇
翻译 4 篇
转载 69 篇
评论 1148 篇
FreeEIM Studio的公告
最近评论
ScanerKi:#include <stdio.h>

int asm(int s)
{
int t=0;
__asm
{
mov eax, DWORD PTR [ebp+8] ;把s的值传给eax
mov t, eax ;把eax的值传给t
}
printf("- %d……
li_delong:谢谢
li_delong:谢谢
li_delong:谢谢
XUETUJIAN:
文章分类
收藏
相册
EIM 截图
相关软件图片
PI的最精确值
FreeEIM 标签
FreeEIM华军下载
XP SP2 SDK
下载FreeEIM
盛天龙
飞鸽传书
不错的网站
CPPBLOG
局域网聊天
泡妞专家
用VC写Assembly代码(RSS)
百度的Blog(RSS)
存档
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

转载  Template Metaprogramming 收藏

新一篇: The C++ Programming Language | 旧一篇: Vista 破解版下载地址[试过可用]

1. 何谓 Metaprogramming?
  • Metaprogram: program that manipulates another program.
  • Metaprogramming is not a new concept:
    • Compiler is a metaprogram: manipulates your code and produces code in a lower level code
    • Preprocessor
    • YACC
这个容易理解,就像 Metadata 是“关于数据的数据”一样,Meta 这个前缀本身是很能说明问题的,如果把编译器也看成是一个 Metaprogram ,那么这个概念的确是没有什么神秘的,计算机里面的绝大多数程序,干的事情都不外乎把这种数据变成另外一种数据。

2. Metaprogramming 的几种途径
  • One approach: external to the language that is being manipulated
  • Another approach: Domain language and host language are the same
第一种就是说的 YACC之类的了,当然,命令解释器也当仁不让的可以算是。
第二种么,Preprocessor 当然算是了,template 则是重头戏了。

3. 举几个简单的例子先?
CppTM 领域最经典最简单的例子莫过于计算阶乘了,它简单而有用,同时体现了 CppTM 的递归本质。我想绝大多数人当年学习递归的时候也是从这个例子开始的:

#include <iostream>

using namespace std;

template <int N>
struct factorial
{
    static const int value = N * factorial<N - 1>::value;
};

template<>
struct factorial<0>
{
    static const int value = 1;
};

int main()
{
    cout << "factorial<10>: " << factorial<10>::value << endl;
    cout << "sizeof char[factorial<4>::value]: " <<
            sizeof(char[factorial<4>::value]) / sizeof(char) << endl;
}

输出:

factorial<10>: 3628800
sizeof char[factorial<4>::value]: 24

这只是一个回顾,上面的简单程序就不用我解释了吧?下面的这个 remove_cv 算法会去掉参数类型的 const 和 volatile 修饰符(如果有的话),Boost type_traits 就是这么干的。

#include <iostream>

using namespace std;

template <class T> struct remove_cv
{ typedef T type; };

template <class T> struct remove_cv<const volatile T>
{ typedef T type; };

template <class T> struct remove_cv<const T>
{ typedef T type; };

template <class T> struct remove_cv<volatile T>
{ typedef T type; };

int main()
{
    cout << "remove_cv<const int>: "
         << typeid(remove_cv<const int>::type).name() << endl;
    cout << "remove_cv<volatile int>: "
         << typeid(remove_cv<volatile int>::type).name() << endl;
    cout << "remove_cv<const volatile int>: "
         << typeid(remove_cv<const volatile int>::type).name() << endl;
}

输出:

remove_cv<const int>: int
remove_cv<volatile int>: int
remove_cv<const volatile int>: int

这个也很简单,但是非常有用。最后再来一个,相当有用的,它让我们可以在编译期间把一个数字作为二进制数来解释:

#include <iostream>

using namespace std;

template <unsigned long N>
struct binary
{
    static unsigned const value =
        binary<N/10>::value << 1 | N%10;
};

template <>
struct binary<0>
{
    static unsigned const value = 0;
};

int main()
{
    cout << "binary<1>: " << binary<1>::value << endl;
    cout << "binary<11>: " << binary<11>::value << endl;
    cout << "binary<101>: " << binary<101>::value << endl;
    cout << "binary<111>: " << binary<111>::value << endl;
    cout << "binary<1011101>: " << binary<1011101>::value << endl;
}

输出:

binary<1>: 1
binary<11>: 3
binary<101>: 5
binary<111>: 7
binary<1011101>: 93

不过上面这个程序不容错,换句话说,如果你写 binary<123>::value ,编译器不会阻止你,还会给出一个愚蠢的答案。如果要做一个容错的解释器,只需要玩一个小小的把戏(本人原创):

#include <iostream>

using namespace std;

namespace aux{
    // 对于 0 和 1 以外的数,都不定义 value ,这样在出现 0 1 之外的数
    // 的时候,编译器会抱怨找不到 value
    template <unsigned long N>
    struct binary
    {};
   
    template <>
    struct binary<1>
    { static unsigned const value = 1; };
   
    template <>
    struct binary<0>
    { static unsigned const value = 0; };
}

template <unsigned long N>
struct binary
{
    static unsigned const value =
        binary<N/10>::value << 1 | aux::binary<N%10>::value;
};


template <>
struct binary<0>
{
    static unsigned const value = 0;
};

int main()
{
    cout << "binary<1>: " << binary<1>::value << endl;
    cout << "binary<11>: " << binary<11>::value << endl;
    cout << "binary<101>: " << binary<101>::value << endl;
    cout << "binary<111>: " << binary<111>::value << endl;
    cout << "binary<1011101>: " << binary<1011101>::value << endl;
    // 你可以 uncomment 下面这一行看看会发生什么
    //cout << "binary<123>: " << binary<123>::value << endl;
}

输出还是一样,但是如果你写了 binary<123>::value 这样的东西,编译器就会抱怨了:

error C2039: 'value' : is not a member of 'aux::binary<N>'
        with
        [
            N=2
        ]
// bla bla bla

好了,例子够多了,我们可以稍微总结一下。CppTM 的好处在于:
  1. 把很多计算放到编译期间完成,使得运行效率大为提高
  2. 由于计算在编译期间完成,很多错误也可以在编译期间发现,程序员不用到了程序开始跑了才进入痛苦的调试
  3. 有一些事情,比如 remove_cv ,在运行期间还的确不那么好做

4. 我怎么开始 Metaprogamming 呢?
在我们开始学习编程的时候,首先学到的是赋值、条件判断、循环等等。在 CppTM 中,这些有了变化,如下:

循环 --> 模板递归
条件判断 --> 模板偏特化
赋值 --> 没有,变量的值一旦确定就不会变化(这对于 functional programming 是常事)
函数输入输出 --> 类型和常量

其实这些特征并不是什么旁门左道,正好相反,它具有 functional programming 的特征,符合图灵机模型(感兴趣的话可以看这篇 paper: C++ Templates are Turing Complete)。

5. 模板偏特化是个好东西,但是我每次都要把 if...then...else 映射成它,岂不是要累死?况且这编码量也太大...
这是个好问题,好在在计算机科学里面有一句箴言:You can solve everything by adding an extra layer of abstraction. 使用模板偏特化进行条件判断如此常用,我们完全应该把它抽象出来以备重用:

template <bool Cond, class Then, class Else>
struct if_
{ typedef Then type; };

template <class Then, class Else>
struct if_ <false, Then, Else>
{ typedef Else type; };

简单吧? 虽然简单,但是我们从此却可以在更高的抽象层面上看问题,我们摆脱了用模板偏特化思考,现在可以直接用 if...then...else 来思考了。那么,上面解释二进制数的程序就变成了下面这样:

#include <iostream>

using namespace std;

template <bool Cond, class Then, class Else>
struct if_
{ typedef Then type; };

template <class Then, class Else>
struct if_ <false, Then, Else>
{ typedef Else type; };


namespace aux{
    struct one
    { static unsigned const value = 1; };
    struct zero
    { static unsigned const value = 0; };
    struct other
    {};
}

template <unsigned long N>
struct binary
{
    static unsigned const value =
        if_ <N/10 == 0, aux::zero, binary<N/10> >::type::value << 1 |
        if_ <N%10 == 0, aux::zero,
            if_ <N%10 == 1, aux::one, aux::other>::type
        >::type::value;
};

int main()
{
    cout << "binary<1>: " << binary<1>::value << endl;
    cout << "binary<11>: " << binary<11>::value << endl;
    cout << "binary<101>: " << binary<101>::value << endl;
    cout << "binary<111>: " << binary<111>::value << endl;
    cout << "binary<1011101>: " << binary<1011101>::value << endl;
    // 你可以 uncomment 下面这一行看看会发生什么
    //cout << "binary<123>: " << binary<123>::value << endl;
}

输出还是一样,但是现在不仅程序长度减少,而且相关的逻辑也用我们熟悉的 if...then...else 的方式来表达。多一层抽象果然威力强大!当然,循环还是要用模板递归的。

6. 流程控制的问题解决了,下面呢?我们是不是需要一些容器,像 STL 那样?
如果要表达一个装“类型”的容器,你会怎么做?象下面这样么?

struct types
{
    typedef int t1;
    typdef long t2;
    typedef std::vector<double> t3;
}

大概只要稍微明智一点,你就会马上放弃这个想法,它太没有通用性了。真正的启示来自于 Lisp ,在 Lisp 中,表是最重要的数据结构,几乎是“万物皆表”。一个表由一个头和一个尾组成,可以嵌套,空表用 nil 表示。这种简单的概念却有着不可思议的表达能力。如果我们用 C++ 来模拟,就是这样:

template <class First, class Rest>
struct cons
{
    typedef First first;
    typedef Rest rest;
};

struct nil {};

现在我们需要表示一个类型列表就有章可循了:

typedef
    cons<int,
    cons<long,
    cons<std::vector<double>,
    nil> > > a_type_list;

它是递归的,从而我们可以很容易的用递归的方式来对它们作协操作,用来操作它们的,就是 Metafunction,前面的 if_ 就是一个 Metafunction。例如我们想选择两个类型中比较大的一个,可以写一个 choose_larger Metafunction:

template <typename T1, typename T2>
struct choose_larger
{
    typedef typename if_ <(sizeof(T1) > sizeof(T2)), T1, T2>::type type;
};

我们再次看到,由于有了 if_ ,我们的生活变得轻松多了。让我们继续向前发展,我们不想仅仅停留在两个类型比大小上,我们希望选择一个 type list 里面最大的那一个:

template <typename T> struct largest;

template <typename First, typename Rest>
struct largest<cons<First, Rest> >
    : choose_larger<First, typename largest<Rest>::type>
{};

template< typename First >
struct largest<cons<First,nil> >
{ typedef First type; };

其实上面的也可以用上 if_ ,只不过我们现在还没有写出判断一个类型是否为 nil 的 Metafunction ,这是件很简单的事情,大家可以自己去写写看。有了它们,得到一个 type list 里面最大的元素就轻而易举了:

#include <vector>
#include <iostream>

using namespace std;

//... 上面的那些 Metafunction

int main()
{
    typedef
        cons<int,
        cons<long,
        cons<std::vector<double>,
        nil> > > type_list;
   
    cout << typeid(largest<type_list>::type).name() << endl;
}

输出:

class std::vector<double,class std::allocator<double> >

7. 的确很棒,但是这跟我们当年学数据结构以后,没事就写个链表玩玩差不多,在生产环境中可不能这样,用什么通用的方法么?
终于到了这一步,在“重复发明轮子”足够多次以后,终于就有人会出来发明通用轮子的,MPL 就是一个很好的尝试,当然 Loki 也算是。
还记得 if_ 让我们尝到的甜头么?聪明人在看到了这些甜头以后,是决不会止步不前的,Dave Abrahams 和 Aleskey Gurtovoy 就是这样的聪明人,他们发明了 MPL 。留到下一篇好了。 

发表于 @ 2007年02月02日 10:59:00|评论(loading...)|编辑

新一篇: The C++ Programming Language | 旧一篇: Vista 破解版下载地址[试过可用]

评论:没有评论。

发表评论  


登录
Csdn Blog version 3.1a
Copyright © FreeEIM Studio