本系列全部转载自kuibyshev.bokee.com
MPL是由David Abrahams和Aleksey Gurtovoy为方便模板元编程而开发的库,2003年被Boost吸纳为其中的一员,此后又历经一些大幅度修改,目前已经相当完善,其最新版本于2004年11月发布。MPL的出现是C++模板元编程发展中的一大创举,它提供了一个通用、高层次的编程框架,其中包括了序列(Sequence)、迭代器(Iterator)、算法(Algorithm)、元函数(Metafunction)等多种组件,具有高度的可重用性,不但提高了模板元编程的效率,而且使模板元编程的应用范围得到相当的扩展。
一个库的组织形式有时候甚至比它的功能还重要。MPL的作者聪明地借鉴了已经取得巨大成功的STL,在MPL中保留了许多STL的概念,对函数式的编程方式进行了精巧的包装,使得任何熟悉STL的程序员都可以轻易地理解MPL的使用方法。像STL一样,MPL有一个完整的概念体系,对组件作了精心的划分,组件之间相对独立,接口具有通用性,因此将组件之间的依存度和耦合性降低到最小的限度。
STL和MPL的组件概念对照如下:
STL概念 |
MPL对应概念 |
容器(Container) |
序列(Sequence) |
算法(Algorithm) |
算法(Algorithm) |
迭代器(Iterator) |
迭代器(Iterator) |
仿函数(Functor) |
元函数类(Metafunction) |
配接器(Adaptor) |
有View、Inserter Iterator和相当于仿函数配接器的Binding元函数 |
配置器(Allocator) |
无此概念 |
标准中没有定义 |
宏(Macro) |
MPL是一个高层次的库,它的地位和编译期执行的特殊性决定了它需要一些特殊的辅助设施,并对其他库会有所依赖。
Preprocessor库是一个基于宏的元编程库[7]。预处理器的作用发生在编译以前,所以它比MPL所处的地位还要高端,能够真正实现代码生成。它的典型功能是迭代或者枚举相似的代码段,减少重复而易写错的代码段。MPL中不少代码是近似的,比如在vector的原始代码中,就需要定义n个
vectori { … }
其中i从1迭代到n。为了减少重复劳动,MPL的源代码大量使用自定义和Preprocessor库的宏对重复或具有递推性的内容进行迭代。不过,这也导致源代码难以阅读。比如上面一段展开后的源代码首先是定义在vector/aux_/numbered.cpp的:
// Preprocessor的宏,得到目前属于第几次迭代
#define i_ BOOST_PP_FRAME_ITERATION(1)
...
template<
//Preprocessor的宏,枚举参数列表
BOOST_PP_ENUM_PARAMS(i_, typename T)
>
// Preprocessor的宏,拼合vector和当前次数
struct BOOST_PP_CAT(vector,i_)
{ ... }
然后为了迭代n个上面的类模板,另一个文件则需要重复include这个文件,利用Preprocessor的文件迭代能力可以这样写:
// Preprocessor的宏,其中第一个参数3表示后面的参数组有3个
//元素,0和10表示迭代的范围是从0到10,最后一个参数是文件
//迭代的文件名
# define BOOST_PP_ITERATION_PARAMS_1 \
(3,(0, 10, ))
// Preprocessor的宏,要求按照上面的指定的参数进行递归
# include BOOST_PP_ITERATE()
尽管如此,宏还是必需的,它不但避免了重复编写递推式的代码(比如在上述的vector类模板中,n可达50之大,如果完全手写确实是浪费时间),而且还有效控制了代码的生成(比如只需要通过定义迭代次数,即可控制实际生成的类模板个数)。实际上,在使用vector(或其他组件)时,通常我们并不需要每次编译都把这些代码重新生成一次,MPL的作者已经充分考虑到编译效率的问题,所以在MPL的代码中,为每个流行的编译器都建立了一个Processed目录,里面存放着针对编译器特点展开了的代码。仅当定义了BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS时才会强制MPL重新用宏来生成代码。
MPL的作者指出,无论喜欢还是不喜欢,目前宏必须在MPL中扮演着这个不可替代的角色。
Type Traits库[9]用于验证传递的参数或参数之间是否符合一定的条件,比如可以判定两个参数是否有继承关系、是否可转换等。
Static Assert库[8]用于编译时断言,用法类似于C中常用的断言assert()。如果参数经编译时的静态计算为true,则代码能通过编译,不会有任何效果,反之,则会编译出错,并且在使编译信息里面包含有“STATIC_ASSERTION_FAILURE”的字样。
Static Assert的底层是接受一个bool参数的模板STATIC_ASSERTION_FAILURE,它对true定义一个有成员的特化模板,对false的情况则只有一个特化的声明(无定义)。其接口是一个宏,它产生的代码是sizeof(STATIC_ASSERTION_FAILURE< ... >),显然当参数的实际结果为false时,编译器无法判断STATIC_ASSERTION_FAILURE的长度,因为它尚未定义。
因为MPL是只在编译时生效的库,用Static Assert来调试程序是非常合适的,它往往与Type Traits库搭配使用。
像STL一样,由于编译器对标准支持不同,为了使程序库具有移植性,最好是针对环境进行预先的设置。对于MPL这种先锋性的库来说,编译器问题更加让库作者相当头痛。借助于对环境的侦查,可以对预先发现的问题,比如模板的局部特化能力、已知的一些编译器的bug等等,采取相应的补救措施[4]。
序列是MPL中的数据结构的统称,是MPL中处于中心地位的组件,其地位相当于STL中的容器。MPL对序列的性质进行了细致的划分:
性质 |
含义 / 主要模型 |
前向序列Forward Sequence |
begin和end元函数能够界定其头尾范围的类型序列 / MPL中所有序列 |
双向序列Bidirectional Sequence |
迭代器属于双向迭代器的前向序列 / vector,range_c |
随机访问序列Random Access Sequence |
迭代器属于随机访问迭代器的双向序列 / vector,range_c |
可扩展序列Extensible Sequence |
允许插入和删除元素的序列 / vector,list |
前可扩展序列Front Extensible Sequence |
允许在前端插入和删除元素的可扩展序列 / vector,list |
后可扩展序列Back Extensible Sequence |
允许在后端插入和删除元素的可扩展序列 / vector,list |
关联序列Associative Sequence |
可以用key值来检索元素的前向序列 / set,map |
可扩展关联序列Extensible Associative Sequence |
允许插入和删除元素的关联序列 / set,map |
整型序列包装器Integral Sequence Wrapper |
存放一系列整型常量类(Integral Constant)的一种类模板 / vector_c,list_c,set_c |
不定序列Variadic Sequence |
可以用给定元素个数或用不指定元素个数的形式来定义的序列 / vector,list,map |
部分概念在现阶段的MPL版本中其实存在着一些冗余,但这种以概念驱动的程序库却是很清晰的:每一种概念的背后都指明了它所支持的操作。
(1)概述
MPL中最简单和最常用的序列就是vector。而deque在目前版本的MPL中相当于vector。vector的实质十分类似于前面示例的类型“数组”,逻辑上是连续线性的,由于它属于不定序列,使用时既可以指定长度,以vectornn>来定义,也可以直接用vectorn>来定义。注意n不能超过宏BOOST_MPL_LIMIT_VECTOR_SIZE的定义,目前MPL的默认值是20。vector的特点是支持尾端常数时间的插入和删除操作以及中段和前端线性时间的插入和删除操作。
(2)操作
vector支持的操作无论在命名还是逻辑上基本都与STL 一致,但有一个重大区别,STL的操作函数定义在类的内部,但是限于模板元编程的特殊性,MPL的这些元函数在容器外定义。下表列出它们的用法:
begin::type |
返回一个迭代器指向v的头部 |
end::type |
返回一个迭代器指向v的尾部 |
size::type |
返回一个v的大小 |
empty::type |
当且仅当v为空时返回一个整型常量类,其值为true |
front::type |
返回v的第一个元素 |
back::type |
返回v的最后一个元素 |
at::type |
返回v的第n个元素 |
insert::type |
返回一个新的vector使其定义为[begin::type, pos), x, [pos, end::type) |
insert_range::type |
返回一个新的vector使其定义为[begin::type, pos), [begin::type, end::type) [pos, end::type) |
erase::type |
返回一个新的vector使其定义为[begin::type, pos), [next::type, end::type) |
erase::type |
返回一个新的vector使其定义为[begin::type, pos), [last, end::type) |
clear::type |
返回一个空的vector |
push_back::type |
返回一个新的vector使其定义为[begin::type, end::type), x |
pop_back::type |
返回一个新的vector使其定义为[begin::type, prior< end::type >::type) |
push_front::typ 发表评论
最新评论
|
评论