C++ Meta Programming 和 Boost MPL(4)

本系列全部转载自kuibyshev.bokee.com

1. Boost中的MPL库分析

MPL是由David AbrahamsAleksey Gurtovoy为方便模板元编程而开发的库,2003年被Boost吸纳为其中的一员,此后又历经一些大幅度修改,目前已经相当完善,其最新版本于200411月发布。MPL的出现是C++模板元编程发展中的一大创举,它提供了一个通用、高层次的编程框架,其中包括了序列(Sequence)、迭代器(Iterator)、算法(Algorithm)、元函数(Metafunction)等多种组件,具有高度的可重用性,不但提高了模板元编程的效率,而且使模板元编程的应用范围得到相当的扩展。

1.1. MPL的组织架构

一个库的组织形式有时候甚至比它的功能还重要。MPL的作者聪明地借鉴了已经取得巨大成功的STL,在MPL中保留了许多STL的概念,对函数式的编程方式进行了精巧的包装,使得任何熟悉STL的程序员都可以轻易地理解MPL的使用方法。像STL一样,MPL有一个完整的概念体系,对组件作了精心的划分,组件之间相对独立,接口具有通用性,因此将组件之间的依存度和耦合性降低到最小的限度。

STLMPL的组件概念对照如下:

STL概念

MPL对应概念

容器(Container

序列(Sequence

算法(Algorithm

算法(Algorithm

迭代器(Iterator

迭代器(Iterator

仿函数(Functor

元函数类(Metafunction

配接器(Adaptor

ViewInserter Iterator和相当于仿函数配接器的Binding元函数

配置器(Allocator

无此概念

标准中没有定义

宏(Macro

1.2. MPL对其他库的依赖

MPL是一个高层次的库,它的地位和编译期执行的特殊性决定了它需要一些特殊的辅助设施,并对其他库会有所依赖。

1.2.1. BoostPreprocessor

Preprocessor库是一个基于宏的元编程库[7]。预处理器的作用发生在编译以前,所以它比MPL所处的地位还要高端,能够真正实现代码生成。它的典型功能是迭代或者枚举相似的代码段,减少重复而易写错的代码段。MPL中不少代码是近似的,比如在vector的原始代码中,就需要定义n

vectori { … }

其中i1迭代到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

//元素,010表示迭代的范围是从010,最后一个参数是文件

//迭代的文件名

# 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中扮演着这个不可替代的角色。

1.2.2. BoostType Traits

Type Traits[9]用于验证传递的参数或参数之间是否符合一定的条件,比如可以判定两个参数是否有继承关系、是否可转换等。

1.2.3. BoostStatic Assert

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库搭配使用。

1.2.4. BoostConfig

STL一样,由于编译器对标准支持不同,为了使程序库具有移植性,最好是针对环境进行预先的设置。对于MPL这种先锋性的库来说,编译器问题更加让库作者相当头痛。借助于对环境的侦查,可以对预先发现的问题,比如模板的局部特化能力、已知的一些编译器的bug等等,采取相应的补救措施[4]

1.3. MPL中的序列

1.3.1. MPL序列概述

序列是MPL中的数据结构的统称,是MPL中处于中心地位的组件,其地位相当于STL中的容器。MPL对序列的性质进行了细致的划分:

性质

含义 / 主要模型

前向序列Forward Sequence

beginend元函数能够界定其头尾范围的类型序列 / MPL中所有序列

双向序列Bidirectional Sequence

迭代器属于双向迭代器的前向序列 / vectorrange_c

随机访问序列Random Access Sequence

迭代器属于随机访问迭代器的双向序列 / vectorrange_c

可扩展序列Extensible Sequence

允许插入和删除元素的序列 / vectorlist

前可扩展序列Front Extensible Sequence

允许在前端插入和删除元素的可扩展序列 / vectorlist

后可扩展序列Back Extensible Sequence

允许在后端插入和删除元素的可扩展序列 / vectorlist

关联序列Associative Sequence

可以用key值来检索元素的前向序列 / setmap

可扩展关联序列Extensible Associative Sequence

允许插入和删除元素的关联序列 / setmap

整型序列包装器Integral Sequence Wrapper

存放一系列整型常量类(Integral Constant)的一种类模板 / vector_clist_cset_c

不定序列Variadic Sequence

可以用给定元素个数或用不指定元素个数的形式来定义的序列 / vectorlistmap

部分概念在现阶段的MPL版本中其实存在着一些冗余,但这种以概念驱动的程序库却是很清晰的:每一种概念的背后都指明了它所支持的操作。

1.3.2. vectordeque

1)概述

MPL中最简单和最常用的序列就是vector。而deque在目前版本的MPL中相当于vectorvector的实质十分类似于前面示例的类型数组,逻辑上是连续线性的,由于它属于不定序列,使用时既可以指定长度,以vectornn>来定义,也可以直接用vectorn>来定义。注意n不能超过宏BOOST_MPL_LIMIT_VECTOR_SIZE的定义,目前MPL的默认值是20vector的特点是支持尾端常数时间的插入和删除操作以及中段和前端线性时间的插入和删除操作。

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

分享到:
评论
zhangyafei_kimi
  • 浏览: 165287 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

你可能感兴趣的:(编程,C++,c,算法,C#)