preprocessor是强大的预处理元编程库,构建了一套预处理期间的数据结构和迭代控制流。
宏的基础知识
宏定义
#define identify replacement_list
定义的identify为宏名,后续文本中出现的identify都会被预处理器替换为对应的replacement_list。
#define identify( a1, a2, …, an ) replacement_list
类似函数的宏,是 “预处理阶段的元函数”。每个ai标识符命名了一个宏形参,当宏名字在后续文本中出现,并且后跟适当的实参列表argument_list,它将被替换为replacement_list,并且其中的参数的位置替换为指定的实参。
宏实参
定义:宏实参列表是以下两种预处理标记(Preprocessing Tokens)的非空序列(逗号分隔):
- 除逗号和小括号之外的预处理标记。 // 不可以是单个逗号或小括号的标记
- 由一对小括号包含的一组预处理标记。 // 括号嵌套的递归语义
注意:
- 预处理标记是指C++语言中认识的任何标记。
- 逗号和小括号有特殊地位,逗号’,’用于分隔标记,小括号对可以包含任何标记。宏实参不能包含没有配对的小括号,也不能包含没有被小括号包含的逗号。
- 其他的标记没有特殊的意思,比如:
[], {}, <>
等没有特殊地位
例子:
#define FOO(x) BOOST_PP_STRINGIZE(x) // 将x实参转化为字符串文本
FOO( , ); // 出错
FOO( ) ); // 出错
FOO((,)); // OK
FOO(()); // OK
preprocessor的设计思想
对于泛型编程来说,特别是提供基础组件,常常会遇到尴尬的局面,比如: 用is_function_tester() 函数来测试某个类型是否可以转换为函数指针。由于参数列表的特殊性,不可能直接匹配一个带有任意参数列表的函数。所以,is_function_tester() 必须为所支持的各个不同的参数数量进行重载。
例如:
template<class R>
yes_type is_function_tester(R (*)());
template<class R, class A0>
yes_type is_function_tester(R (*)(A0));
template<class R, class A0, class A1>
yes_type is_function_tester(R (*)(A0, A1));
template<class R, class A0, class A1, class A2>
yes_type is_function_tester(R (*)(A0, A1, A2));
// ...
分析下,这种写法的弊端:
- 手动书写不仅低效,而且容易出错。
- 需要扩展很麻烦
考虑,最好让代码来生成这些代码,而这种情况是元编程无能为力的,它已经在编译期之前了,让其他程序来生成代码拷贝进来显然是不大合理的。
- boost库preprocessor带给了我们预处理期间强大的处理能力。轻松处理这些问题。
- boost中的大量的库使用了preprocessor。(使代码量减少、可维护性大大加强)。没有preprocessor,很多库的实现是非常麻烦的。(比如:boost.funcion和boost等),还有你可以看一下Loki库没有使用preprocessor,它为了预定义TypeList的类型列表TYPE_LIST_N,文件是多么庞大和繁琐,尽管它已经使用了
#define
的小的简化技巧。
boost库自带的preprocessor的参考reference也非常好,详细信息可以查阅,很方便。本文档只是分析了它的核心内容,以及按照重点进行分类,分析。
数据结构抽象及其操作函数
宏实参列表,类似于函数参数列表(arg1, arg2, …, argn),规则如上定义,将合法的实参进行抽象、规范成合理的“数据类型”,将使用预处理的行为更规范、更可控、更丰富。
Tuple元组
tuple 是一个简单的在括号中用逗号分隔的元素列表。例如, (a, b, c) tuple的操作非常简单高效,但是预定义的元组的操作宏都需要指定元组长度。
- BOOST_PP_TUPLE_ELEM(size, i, tuple) 从tuple中取出第i个元素
size: tuple 的大小。有效的 tuple 大小范围为 0 到 BOOST_PP_LIMIT_TUPLE(默认为25).
i: 要取出的元素在 tuple 中的索引,从零起计。有效值为 0 到 size - 1.
tuple: 要取出元素的 tuple.
例如:
#define TUPLE (a, b, c, d)
BOOST_PP_TUPLE_ELEM(4, 0, TUPLE) // 展开为 a
- BOOST_PP_TUPLE_TO_LIST(size, tuple) 宏将一个 tuple 转换为链表。
例如:BOOST_PP_TUPLE_TO_LIST(3, (x, y, z)) // 展开为 (x, (y, (z, BOOST_PP_NIL)))
- BOOST_PP_TUPLE_TO_SEQ(size, tuple) 将一个 tuple 转换为 seq序列.
例如:BOOST_PP_TUPLE_TO_SEQ(3, (a, b, c)) // 展开为 (a)(b)©
- BOOST_PP_TUPLE_REM(size) 宏展开为一个宏,该宏能去掉一个指定大小的 tuple 的括号。
例如:BOOST_PP_TUPLE_REM(3)(x, y, z) // 展开为 x, y, z
- BOOST_PP_TUPLE_REM_CTOR(size, tuple) 去掉一个指定大小的 tuple 的括号。
例如:BOOST_PP_TUPLE_REM_CTOR(3, (x, y, z)) // 展开为 x, y, z
BOOST_PP_TUPLE_REVERSE(size, tuple) 将一个指定大小的 tuple 反序为新的tuple。
BOOST_PP_TUPLE_EAT(size) 展开为一个宏,该宏将吞掉一个指定长度的tuple。
例如:BOOST_PP_TUPLE_EAT(2)( 1,2 ) 展开为空
Array数组
数组 是一个包含两元素 tuple 的数据结构。第一个元素是数组中的元素数量。第二个元素是由数组中的元素所组成的 tuple. 例如,(3, (a, b, c)) ,长度为3,元素放在tuple中 数组的最大优势在于保存了自己的大小。因此,访问元素时不需要指定大小。只需要在给定的索引位置上有相应元素就可以了。 使用数组可以使得宏的参数数量可变,同时还允许数据声明改变大小,而无需用户明确知道该大小。
BOOST_PP_ARRAY_SIZE(array) 展开为传入的数组的大小。
BOOST_PP_ARRAY_DATA(array) 从一个数组中提取tuple数据。
例如:
#define ARRAY (3, (x, y, z))
BOOST_PP_ARRAY_DATA(ARRAY) // 展开为 (x, y, z)
- BOOST_PP_ARRAY_ELEM(i, array) 从一个数组中提取第i个元素。
例如:
#define ARRAY (4, (a, b, c, d))
BOOST_PP_ARRAY_ELEM(0, ARRAY) // 展开为 a
- BOOST_PP_ARRAY_INSERT(array, i, elem) 向一个数组中插入一个元素(原数组第i个位置之前)。
例如:
#define ARRAY (3, (a, b, d))
BOOST_PP_ARRAY_INSERT(ARRAY, 2, c) // 展开为 (4, (a, b, c, d))
该宏在内部使用了 BOOST_PP_WHILE. 因此,要使用从另一个使用了 BOOST_PP_WHILE 的宏传入的 d 参数,需要使用 BOOST_PP_ARRAY_INSERT_D.。
- BOOST_PP_ARRAY_INSERT_D(d, array, i, elem) 向一个数组插入一个元素。它以最高的效率重入 BOOST_PP_WHILE.
d: 下一个可用的 BOOST_PP_WHILE 迭代。
- BOOST_PP_ARRAY_POP_BACK(array) 从一个数组的尾部弹出一个元素。
该宏返回删除最后一个元素后的数组。如果数组中没有元素,则该宏的结果为未定义。
例如:
#define ARRAY (3, (a, b, c))
BOOST_PP_ARRAY_POP_BACK(ARRAY) // 展开为 (2, (a, b))
该宏在内部使用了 BOOST_PP_REPEAT. 因此,要使用从其它使用了 BOOST_PP_REPEAT 的宏所传入的 z 参数,需使用 BOOST_PP_ARRAY_POP_BACK_Z
- BOOST_PP_ARRAY_POP_BACK_Z(z, array) 从一个数组的尾部弹出一个元素。它以最高的效率重入 BOOST_PP_REPEAT.
z: 下一个可用的 BOOST_PP_REPEAT 维度。
BOOST_PP_ARRAY_POP_FRONT(array) 从一个数组的头部弹出一个元素。
BOOST_PP_ARRAY_POP_FRONT_Z(z, array) 从一个数组的头部弹出一个元素。以最高的效率重入 BOOST_PP_REPEAT.
BOOST_PP_ARRAY_PUSH_BACK(array, elem) 向一个数组的尾部增加一个元素。
BOOST_PP_ARRAY_PUSH_FRONT(array, elem) 向一个数组的头部增加一个元素。
BOOST_PP_ARRAY_REMOVE(array, i) 从一个数组中删除第i个元素。
i: 被删除的元素在数组中的位置,从零起计。有效值为 0 到 BOOST_PP_ARRAY_SIZE(array) - 1.
例如:
#define ARRAY (3, (a, b, d))
BOOST_PP_ARRAY_REMOVE(ARRAY, 2) // 展开为 (2, (a, b))
BOOST_PP_ARRAY_REMOVE_D(d, array, i) 从一个数组中删除一个元素。它以最高的效率重入 BOOST_PP_WHILE.
BOOST_PP_ARRAY_REPLACE(array, i, elem) 替换数组中的某个元素。
BOOST_PP_ARRAY_REPLACE_D(d, array, i, elem) 替换数组中的某个元素。它以最高的效率重入 BOOST_PP_WHILE.
BOOST_PP_ARRAY_REVERSE(array) 将一个数组中的元素反序。
例如:
#define ARRAY (3, (a, b, c))
BOOST_PP_ARRAY_REVERSE(ARRAY) // 展开为 (3, (c, b, a))
序列 seq
序列 (简称为 seq)是一组相邻的带括号的元素。例如, (a)(b)©(d) seq 不能为空。因此,一个 “空的” seq 被认为是一种特殊情况,必须由C++单独处理。 对于序列的处理效率是非常高的,可以认为是随机访问的。
- BOOST_PP_SEQ_HEAD(seq) 展开为一个 seq 的第一个元素。
BOOST_PP_SEQ_TAIL(seq) 展开为一个 seq 中除了第一个元素以外的其它元素。(还是一个序列)
#define SEQ (a)(b)(c) BOOST_PP_SEQ_HEAD(SEQ) // 展开为 a BOOST_PP_SEQ_TAIL(SEQ) // 展开为 (b)(c)
BOOST_PP_SEQ_CAT(list) 串接一个 seq 中的所有元素。
例如:
#define SEQ (a)(b)(c)
BOOST_PP_SEQ_CAT(SEQ) // 展开为 abc
要达到最高效率,请使用BOOST_PP_SEQ_CAT_S(s, seq)。
- BOOST_PP_SEQ_CAT_S(s, seq) 它以最高的效率重入 BOOST_PP_SEQ_FOLD_LEFT.
s: 下一个可用的 BOOST_PP_SEQ_FOLD_LEFT 折叠(fold)步骤。
BOOST_PP_SEQ_ELEM(i, seq) 从一个 seq 中取出一个元素。i范围0 到 BOOST_PP_SEQ_SIZE(seq) - 1 之间
BOOST_PP_SEQ_ENUM(seq) 列出一个 seq 中的所有元素(逗号分隔的标记)。
例如:
#define SEQ (B)(O)(O)(S)(T)
BOOST_PP_SEQ_ENUM(SEQ) // 展开为 B, O, O, S, T
- BOOST_PP_SEQ_FILTER(pred, data, seq) 根据给定的标准过滤一个 seq,展开结果为过滤后的序列
pred: 形如 pred(d, data, elem) 三元谓词。该谓词被 BOOST_PP_SEQ_FILTER 对 seq 中的每个元素进行展开,以下一个可用的 BOOST_PP_SEQ_FOLD_LEFT 折叠步骤(d,但是我们自己不需要直接使用,只要随它放着就行)、一个辅助数据 data 以及 seq 中的当前元素 elem 为参数。该宏必须返回一个范围 0 到 BOOST_PP_LIMIT_MAG 间的整数值。如果该谓词对于某个元素展开为非零,则该元素被包含在结果 seq 中。
data: 传递给 pred 的数据。
例如:
#define SEQ (1)(3)(2)(5)
#define PRED(s, data, elem) BOOST_PP_LESS_EQUAL(elem, data) // 展开返回小于等于的判断值
BOOST_PP_SEQ_FILTER(PRED, 3, SEQ) // 展开为 (1)(3)(2)
要达到最高的效率,请使用 BOOST_PP_SEQ_FILTER_S.。
BOOST_PP_SEQ_FILTER_S(s, pred, data, seq) 根据标准过滤一个seq. 以最高的效率重入 BOOST_PP_SEQ_FOLD_LEFT.
BOOST_PP_SEQ_FIRST_N(n, seq) 展开为一个 seq,由另一个 seq 的头 n 个元素组成。
BOOST_PP_SEQ_FOLD_LEFT(op, state, seq) 从左至右对一个 seq 的元素进行折叠fold。
op: 形如 op(d, state, elem) 的三元操作,每次会生成新的状态,惊且作为下次op的状态参数。该宏将被对于 seq 中的每个元素进行调用,每次返回一个新的 state. 该操作被 BOOST_PP_SEQ_FOLD_LEFT 展开,参数分别为下一个可用的折叠步骤、当前的 state, 以及当前元素。
state: 进行折叠的初始状态。
对于 seq, (0)(1)(2), 该宏展开为: op(d, op(d, op(d, state, 0), 1), 2)
例如:
#define SEQ (b)(o)(o)(s)(t)
#define OP(s, state, x) BOOST_PP_CAT(state, x)
BOOST_PP_SEQ_FOLD_LEFT(OP, BOOST_PP_SEQ_HEAD(SEQ), BOOST_PP_SEQ_TAIL(SEQ)) // 展开为 boost
BOOST_PP_SEQ_FOLD_LEFT_ ## s(op, state, seq)
功能与BOOST_PP_SEQ_FOLD_LEFT完全一致
从左至右对一个 seq 的元素进行折叠fold。它以最高的效率重入 BOOST_PP_SEQ_FOLD_LEFT.
例如:
#define S1 (a)(b)(c)
#define S2 (S1)(S1)(S1)
#define OP(s, state, x) state (BOOST_PP_SEQ_FOLD_LEFT_ ## s(OP_2, _, x))
#define OP_2(s, state, x) BOOST_PP_CAT(state, x)
BOOST_PP_SEQ_FOLD_LEFT(OP, BOOST_PP_SEQ_NIL, S2) // 展开为 (_abc)(_abc)(_abc)
注意:S2序列的每个元素都是S1 -> (a)(b)(c)
,于是BOOST_PP_SEQ_FOLD_LEFT_ ## s(OP_2, _, x)
每次被转成字符串_abc
- BOOST_PP_SEQ_FOLD_RIGHT(op, state, seq) 从右至左对一个 seq 的元素进行折叠fold。
对于 seq, (0)(1)(2), 该宏展开为: op(s, op(s, op(s, state, 2), 1), 0)
BOOST_PP_SEQ_FOLD_RIGHT_ ## s(op, state, seq)
功能一致,它以最高的效率重入 BOOST_PP_SEQ_FOLD_RIGHT.BOOST_PP_SEQ_FOR_EACH(macro, data, seq) 对一个 seq 序列中的每个元素重复调用一个指定的宏。
macro: 形如 macro(r, data, elem) 的三元宏。该宏是一个迭代结构。如果 seq 为 (a)(b)©, 则它展开为序列: macro(r, data, a) macro(r, data, b) macro(r, data, c)
#define SEQ (w)(x)(y)(z)
#define MACRO(r, data, elem) BOOST_PP_CAT(elem, data)
BOOST_PP_SEQ_FOR_EACH(MACRO, _, SEQ) // 展开为 w_ x_ y_ z_
要达到最高效率,请使用 BOOST_PP_SEQ_FOR_EACH_R.
- BOOST_PP_SEQ_FOR_EACH_R(r, macro, data, seq)
以最高的效率重入 BOOST_PP_FOR,可以使用在比如BOOST_PP_SEQ_FOR_EACH的三元宏macro中,因为该宏的定义中,就带一个额外的r可以使用在BOOST_PP_SEQ_FOR_EACH_R中,如果内部还需要FOR_EACH的话。
r: 下一个可用的 BOOST_PP_FOR 迭代值。
macro: 形如 macro(r, data, elem) 的三元宏。该宏由 BOOST_PP_SEQ_FOR_EACH 对 seq 中的每个元素展开。展开的参数为下一个可用的 BOOST_PP_FOR 迭代值、辅助数据 data, 以及当前元素。
data: 传给 macro 的辅助数据。
seq: macro 将对其每个元素进行调用的 seq.
- BOOST_PP_SEQ_FOR_EACH_I(macro, data, seq)
对一个 seq 中的每个元素重复调用一个指定的宏(宏函数包括序号) 形如 macro(r, data, i, elem) 的四元宏。该宏由 BOOST_PP_SEQ_FOR_EACH_I 对 seq 中的每个元素展开。 该宏是一个迭代结构。
如果 seq 为 (a)(b)©, 则它展开为序列: macro(r, data, 0, a) macro(r, data, 1, b) macro(r, data, 2, c) 要达到最高效率,请使用 BOOST_PP_SEQ_FOR_EACH_I_R.
- BOOST_PP_SEQ_FOR_EACH_I_R(r, macro, data, seq)
对一个 seq 中的每个元素重复调用一个指定的宏。它以最高的效率重入 BOOST_PP_FOR. 该宏是一个迭代结构。
如果 seq 为 (a)(b)©, 则它展开为序列: macro(r, data, 0, a) macro(r, data, 1, b) macro(r, data, 2, c)
- BOOST_PP_SEQ_FOR_EACH_PRODUCT(macro, seqs) 对多个 seqs 的每个笛卡尔积重复某个宏。
macro: 形如 macro(r, product) 的二元宏。该宏被 BOOST_PP_FOR_EACH_PRODUCT 以 seqs 中的每个笛卡尔积进行展开。展开时的参数为下一个可用的 BOOST_PP_FOR 迭代值以及一个包含一个笛卡尔积的 seq.
seqs: 多个 seqs 组成的 seq,将从中获得笛卡尔积。
如果两个 seqs 分别为 (a)(b)© 和 (x)(y)(z), 则该宏将生成以下序列:
macro(r, (a)(x)) macro(r, (a)(y)) macro(r, (a)(z)) \
macro(r, (b)(x)) macro(r, (b)(y)) macro(r, (b)(z)) \
macro(r, (c)(x)) macro(r, (c)(y)) macro(r, (c)(z))
例如:
#define S1 (a)(b)(c)
#define S2 (x)(y)(z)
#define S3 (p)(q)
#define MACRO(r, product) BOOST_PP_SEQ_TO_TUPLE(product)
BOOST_PP_SEQ_FOR_EACH_PRODUCT(MACRO, (S1)(S2)(S3))
// 展开为:
// (a, x, p) (a, x, q) (a, y, p) (a, y, q) (a, z, p) (a, z, q)
// (b, x, p) (b, x, q) (b, y, p) (b, y, q) (b, z, p) (b, z, q)
// (c, x, p) (c, x, q) (c, y, p) (c, y, q) (c, z, p) (c, z, q)
- BOOST_PP_SEQ_FOR_EACH_PRODUCT_R(r, macro, seqs)
对多个 seqs 的每个笛卡尔积重复某个宏。它以最高的效率重入 BOOST_PP_FOR.
BOOST_PP_SEQ_INSERT(seq, i, elem) 向 seq 插入一个元素(索引 i 的元素之前)。
BOOST_PP_SEQ_NIL 是一个空 seq 的占位符宏。仅当它被添加到该空 seq 的末尾时有效。
该宏是一个工具宏,表示一个添加到尾部的空起始点。它不是一个空的 seq. 当向该宏添加一个元素时,它展开为该元素 – 即删除其自身。例如,BOOST_PP_SEQ_NIL(x) 和 BOOST_PP_SEQ_PUSH_BACK(BOOST_PP_SEQ_NIL, x) 均展开为 (x).
如果对任意 BOOST_PP_SEQ_*
宏(除了 BOOST_PP_SEQ_PUSH_BACK)以一个包含 BOOST_PP_SEQ_NIL 的参数来调用,行为将是未定义的,多数情况下会导致晦涩的错误。
用于从头部进行添加的,与 BOOST_PP_SEQ_NIL 最为接近的东西是 BOOST_PP_EMPTY. 毕竟所有元素都是预先准备好的,可以通过对尾部调用空的括号来删除 BOOST_PP_EMPTY(也是一种成为调用的技巧). 和 BOOST_PP_SEQ_NIL 一样,将包含 BOOST_PP_EMPTY 的参数传递给任意 BOOST_PP_SEQ_*
宏(除了 BOOST_PP_SEQ_PUSH_FRONT)都是未定义的。
(也可以由一个额外的元素开始,然后在添加完成后将它弹出。)
例如:
BOOST_PP_SEQ_PUSH_BACK( BOOST_PP_SEQ_PUSH_BACK(BOOST_PP_SEQ_NIL, a), b)// 展开为 (a)(b)
BOOST_PP_SEQ_PUSH_FRONT( BOOST_PP_SEQ_PUSH_FRONT(BOOST_PP_EMPTY, a), b)()// 展开为 (b)(a)
其他的一些序列操作宏:
- BOOST_PP_SEQ_SIZE(seq) 展开为一个 seq 的大小。
BOOST_PP_SEQ_SUBSEQ(seq, i, len) 展开为一个 seq 中的子序列。
BOOST_PP_SEQ_POP_BACK(seq) 从一个 seq 的尾部弹出一个元素。
BOOST_PP_SEQ_POP_FRONT(seq) 从一个 seq 的前部弹出一个元素。
BOOST_PP_SEQ_PUSH_BACK(seq, elem) 将一个元素压入 seq 的尾部。
BOOST_PP_SEQ_PUSH_FRONT(seq, elem) 将一个元素压入 seq 的头部。
BOOST_PP_SEQ_REMOVE(seq, i) 从 seq 中删除一个元素。
BOOST_PP_SEQ_RPLACE(seq, i, elem) 宏替换 seq 中的一个元素。
BOOST_PP_SEQ_REST_N(n, seq) 展开为一个 seq,由另一个 seq 的除了头 n 个元素以外的其它元素组成。
BOOST_PP_SEQ_REVERSE(seq) 将一个 seq 反序
BOOST_PP_SEQ_REVERSE_S(s, seq) 一个 seq 反序。它以最高的效率重入 BOOST_PP_SEQ_FOLD_LEFT.
BOOST_PP_SEQ_TO_ARRAY(seq) 将一个 seq 转换为数组。
BOOST_PP_SEQ_TO_TUPLE(seq) 将一个 seq 转换为 tuple.
BOOST_PP_SEQ_TRANSFORM(op, data, seq) 根据指定的转化操作对 seq 中的每个元素进行转化。
op(d, data, elem) 的三元操作。该转化操作被 BOOST_PP_SEQ_TRANSFORM 对 seq 中的每个元素展开 该宏针对 seq 中的每个元素展开 op. 它以每次调用的结果来构建一个新的 seq。 例如,如果 seq 为 (a)(b)©, 则该宏展开为:(op(d, data, a)) (op(d, data, b)) (op(d, data, c))
- BOOST_PP_SEQ_TRANSFORM_S(s, op, data, seq) 根据指定的转化操作对 seq 中的每个元素进行转化。它以最高效率重入 BOOST_PP_SEQ_FOLD_LEFT (功能基本与BOOST_PP_SEQ_TRANSFORM一致)
链表 List
链表 是一个带有头部和尾部的简单的 cons-style 列表。链表的头部为一个元素,尾部为另一个链表或 BOOST_PP_NIL. 例如, (a, (b, (c, BOOST_PP_NIL))) 但是链表的效率和可读性都很差,不推荐。
BOOST_PP_LIST_FIRST(list) 展开为链表的头元素。
BOOST_PP_LIST_REST(list) 展开为一个链表的尾部。
// 其他的一些有用的宏
BOOST_PP_LIST_APPEND(a, b) 对两个链表进行链接。
BOOST_PP_LIST_AT(list, index) 从一个链表中取出一个元素。
BOOST_PP_LIST_CAT(list) 将一个链表中的所有元素串接起来。
BOOST_PP_LIST_ENUM(list) 将一个链表转换为一个逗号分隔的列表。
BOOST_PP_LIST_FILTER(pred, data, list) 根据给定的标准过滤一个链表。
BOOST_PP_LIST_FOLD_LEFT(op, state, list) 从左至右对一个链表的元素进行折叠fold(或叠加)。
BOOST_PP_LIST_FOLD_RIGHT(op, state, list) 从右至左对一个链表的元素进行折叠fold(或叠加)。
BOOST_PP_LIST_FOR_EACH(macro, data, list) 对一个链表中的每个元素重复某个宏。
BOOST_PP_LIST_REST_N(count, list) 展开为一个由某个链表中除了头 count 个元素以外的其它元素所组成的链表。
BOOST_PP_LIST_REVERSE(list) 展开为某个链表的反序。
BOOST_PP_LIST_SIZE(list) 展开为一个链表的大小。
BOOST_PP_LIST_TO_TUPLE(list) 将一个链表转换为 tuple.
BOOST_PP_LIST_TRANSFORM(op, data, list) 根据指定的转化操作对链表中的每个元素进行转化。
重要的宏“算法”说明
数值、逻辑函数
算术运算
BOOST_PP_ADD(x,y) x + y
BOOST_PP_DEC(x) x - 1
BOOST_PP_DIV(x,y) x / y
BOOST_PP_INC(x) x + 1
BOOST_PP_MOD(x,y) x % y
BOOST_PP_MUL(x,y) x * y
BOOST_PP_SUB(x,y) x – y
整型逻辑运算
BOOST_PP_AND(x,y) x && y
BOOST_PP_NOR(x,y) !(x || y)
BOOST_PP_OR(x,y) x || y
BOOST_PP_XOR(x,y) (bool)x != (bool)y ? 1 : 0
BOOST_PP_NOT(x) x ? 0 : 1
BOOST_PP_BOOL(x) x ? 1 : 0
位逻辑运算(单个位操作的)
BOOST_PP_BITAND(x,y) x && y
BOOST_PP_BITNOR(x,y) !(x || y)
BOOST_PP_BITOR(x,y) x || y
BOOST_PP_BITXOR(x,y) (bool)x != (bool)y ? 1 : 0
BOOST_PP_COMPL(x) x ? 0 : 1
比较运算
BOOST_PP_EQUAL(x,y) x == y ? 1 : 0
BOOST_PP_NOT_EQUAL(x,y) x != y ? 1 : 0
BOOST_PP_LESS(x,y) x < y ? 1 : 0
BOOST_PP_LESS_EQUAL(x,y) x <= y ? 1 : 0
BOOST_PP_GREATER(x,y) x > y ? 1 : 0
BOOST_PP_GREATER_EQUAL(x,y) x >= y ? 1 : 0
横向枚举生成
- BOOST_PP_ENUM(count, macro, data) 生成一个以逗号分隔的列表(与repeat的区别就在于逗号分隔)。
宏展开为一个以逗号分隔的序列: macro(z, 0, data), macro(z, 1, data), … macro(z, count - 1, data)
- BOOST_PP_ENUM_PARAMS(count, param) 生成一个以逗号分隔的参数列表。
宏展开为一个以逗号分隔的序列: param ## 0, param ## 1, ... param ## count - 1
- BOOST_PP_ENUM_BINARY_PARAMS(count, p1, p2) 生成一个以逗号分隔的二元参数列表。(常用于一串类型和变量)
宏展开为一个以逗号分隔的序列: p1 ## 0 p2 ## 0, p1 ## 1 p2 ## 1, ... p1 ## count - 1 p2 ## count - 1
- BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(count, param, def) 生成一个以逗号分隔的带有相同缺省值的参数列表。
宏展开为一个以逗号分隔的序列: param ## 0 = def, param ## 1 = def, ... param ## count - 1 = def
- BOOST_PP_ENUM_PARAMS_WITH_DEFAULTS(count, param, def) 生成一个以逗号分隔的带有缺省值的参数列表。
宏展开为一个以逗号分隔的序列: param ## 0 = def ## 0, param ## 1 = def ## 1, ... param ## count - 1 = def ## count - 1
- BOOST_PP_ENUM_SHIFTED(count, macro, data) 生成一个以逗号分隔的、移1位后的列表。
宏展开为一个以逗号分隔的序列: macro(z, 1, data), … macro(z, count - 1, data)
- BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(count, p1, p2) 生成一个以逗号分隔的、移位后的二元参数列表。
宏展开为一个以逗号分隔的序列: p1 ## 1 p2 ## 1, p1 ## 2 p2 ## 2, ... p1 ## count - 1 p2 ## count - 1
- BOOST_PP_ENUM_SHIFTED_PARAMS(count, param)
宏展开为一个以逗号分隔的序列: param ## 1, ... param ## count - 1
- BOOST_PP_ENUM_TRAILING(count, macro, data) 生成一个以逗号分隔的列表,它以逗号开头
宏展开为一个以逗号分隔的序列: , macro(z, 0, data), macro(z, 1, data), … macro(z, count - 1, data)
- BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(count, p1, p2)
宏展开为一个以逗号分隔的序列: , p1 ## 0 p2 ## 0, p1 ## 1 p2 ## 1, ... p1 ## count - 1 p2 ## count - 1
- BOOST_PP_ENUM_TRAILING_PARAMS(count, param)
展开:, param ## 0, param ## 1, ... param ## count - 1
重复repeat
- BOOST_PP_REPEAT(count, macro, data) 表示一个快速水平重复结构。
count: 重复调用 macro 的次数。而macro(z, n, data) 是三元操作
该宏展开为序列:
macro(z, 0, data) macro(z, 1, data) ... macro(z, count - 1, data)
传给 macro 的 z 值代表下一个可用的迭代维度。
- BOOST_PP_REPEAT_FROM_TO(first, last, macro, data)
宏展开为序列: macro(z, first, data) macro(z, first + 1, data) … macro(z, last - 1, data)
- BOOST_PP_REPEAT_FROM_TO_D(d, first, last, macro, data) 表示一个快速水平重复结构。它以最高的效率重入 BOOST_PP_WHILE.
宏展开为序列: macro(z, first, data) macro(z, first + 1, data) … macro(z, last - 1, data)
三个高效重入版本:
BOOST_PP_REPEAT_ ## z(count, macro, data)
BOOST_PP_REPEAT_ ## z(count, macro, data)
BOOST_PP_REPEAT_FROM_TO_D_z(d, first, last, macro, data)
迭代控制、结构控制
BOOST_PP_EXPR_IF(cond, expr) 展开为它的第二个参数,如果第一个参数非零,否则展开为空。
BOOST_PP_EXPR_IIF(bit, expr) 展开为它的第二个参数,如果第一个参数为 1;如果第一个参数为 0 则展开为空。
bit: 确定结果为 expr 或为空的条件。该值必须展开为 0 或 1.
该宏不会对其第一个参数执行布尔类型转换。如果需要转换,请改用 BOOST_PP_EXPR_IF
- BOOST_PP_FOR(state, pred, op, macro) 表示泛化的for水平重复结构。
state : 初始状态。
pred : 判断是否继续展开,形如 pred(r, state) 的二元谓词。该宏必须展开为一个位于 0 到 BOOST_PP_LIMIT_MAG 间的整数。当该谓词返回非零时,BOOST_PP_FOR 重复展开 macro. 该宏被调用时参数为下一个可用的 BOOST_PP_FOR 迭代以及当前 state.
op : 操作生成新的状态,形如 op(r, state) 的二元操作。该操作被 BOOST_PP_FOR 展开,参数为下一个可用的 BOOST_PP_FOR 迭代和当前 state. 该宏被重复应用于 state, 每次产生一个新的 state, 直至 pred 返回 0.
macro: 利用state生成做后的输出,形如 macro(r, state) 的二元宏。该宏被 BOOST_PP_FOR 展开,参数为下一个可用的 BOOST_PP_FOR 迭代和当前 state. 该宏被 BOOST_PP_FOR 重复调用,直至 pred 返回 0.
宏展开为序列: macro(r, state) macro(r, op(r, state)) … macro(r, op(r, … op(r, state) … ))
例如:
#define PRED(r, state) \
BOOST_PP_NOT_EQUAL( \ // 首元素不等于第二元素加1
BOOST_PP_TUPLE_ELEM(2, 0, state), \
BOOST_PP_INC(BOOST_PP_TUPLE_ELEM(2, 1, state)) \
)
#define OP(r, state) \
( \
BOOST_PP_INC(BOOST_PP_TUPLE_ELEM(2, 0, state)), \ // 递增首元素
BOOST_PP_TUPLE_ELEM(2, 1, state) \ // 取出第二元素 ,组合成新的元组(state)
)
#define MACRO(r, state) BOOST_PP_TUPLE_ELEM(2, 0, state) // 取元组的首元素
BOOST_PP_FOR((5, 10), PRED, OP, MACRO) // 展开为 5 6 7 8 9 10(当状态为(11,10)停止,不输出)
BOOST_PP_FOR_ ## r(state, pred, op, macro)
表示一个重入到 BOOST_PP_FOR 的迭代结构。BOOST_PP_WHILE(pred, op, state) while循环结构。
pred: 形如 pred(d, state) 的二元谓词,用于判断迭代的终点。该谓词被 BOOST_PP_WHILE 展开,以下一个可用的迭代值 d 以及当前的 state 为参数。该谓词必须展开为一个在范围 0 到 BOOST_PP_LIMIT_MAG 之间的值。该结构持续进行循环,直至该谓词返回 0. 当该谓词返回 0, BOOST_PP_WHILE 返回当前的 state.
op: 形如 op(d, state) 的二元操作,每次生成新的状态state,而最后pred为0的状态记为展开的结果。该操作被 BOOST_PP_WHILE 展开,以下一个可用的迭代值 d 以及当前的 state 为参数。该宏被重复应用于 state, 每次生成一个新的 state, 直至 pred 返回 0.
state: 初始的状态。通常该参数为一个 tuple.(这样最后的操作还是一个tuple) 该宏当 pred(d, state) 为非零时迭代 op(d, state)。换言之,它展开为: op(d, … op(d, op(d, state)) … ).
BOOST_PP_WHILE_ ## d(pred, op, state)
表示一个重入到 BOOST_PP_WHILE 的循环结构。
其他
BOOST_PP_EMPTY() 是一个无参工具宏,展开为空
BOOST_PP_IDENTITY(item)() 展开为其调用时的参数。
它的实现:#define BOOST_PP_IDENTITY(item) item BOOST_PP_EMPTY
BOOST_PP_CAT(a, b) 对其参数的展开结果进行连接。(代替
##
)BOOST_PP_APPLY(x) 抽象了有参数与无参数之间的差异。
如果 x 为 BOOST_PP_NIL, 该宏展开为空。如果 x 为一个单元素的 tuple, 则展开为 tuple 中的内容。
- BOOST_PP_COMMA() 展开为一个逗号
BOOST_PP_COMMA_IF(cond) 果 cond 展开为 0, 则该宏展开为空。否则展开为一个逗号。
BOOST_PP_EXPAND(x) 对其参数执行一个双重展开。(普通参数直接返回本身)
#define MACRO(a, b, c) (a)(b)(c) #define ARGS() (1, 2, 3) BOOST_PP_EXPAND(MACRO ARGS()) // 展开为 (1)(2)(3)
BOOST_PP_LPAREN() 展开为一个左括号。
BOOST_PP_LPAREN_IF(cond) 依条件展开为一个左括号。
BOOST_PP_RPAREN() 展开为一个右括号。
BOOST_PP_RPAREN_IF(cond)
BOOST_PP_STRINGIZE(text) 将其参数展开后进行字符串化。
BOOST_PP_WSTRINGIZE(text) 将其参数展开后进行宽字符串化。(代替#…)
重要的能力展示
横向重复
关键是利用BOOST_PP_REPEAT(count, macro, data),因为它能重复横向扩充count次,macro(z, 0, data) macro(z, 1, data) … macro(z, count - 1, data) ,如果将macro定义为有用的定义(注意repeat的宏扩展之间没有逗号的,适合于生成代码),比如一些类型基本一致的模板类特化,将是非常简洁有效的。
例如:
// 供BOOST_PP_ENUM调用的宏,与BOOST_PP_REPEAT的唯一区别是它的展开之间有逗号
#define TINY_print(z, n, data) data
// 供BOOST_PP_REPEAT调用的宏(扩展之间没有逗号)
#define TINY_size(z, n, unused) \
template <BOOST_PP_ENUM_PARAMS(n, class T)> \
struct tiny_size< \
BOOST_PP_ENUM_PARAMS(n,T) \
BOOST_PP_COMMA_IF(n) \
BOOST_PP_ENUM( \
BOOST_PP_SUB(TINY_MAX_SIZE,n), TINY_print, none) \
> \
: mpl::int_<n> {};
// 重复扩展TINY_MAX_SIZE次(宏参数为 0 ~ TINY_MAX_SIZE-1)
BOOST_PP_REPEAT(TINY_MAX_SIZE, TINY_size, ~)
// 扩展代码之后,局部宏不需要了
#undef TINY_size
#undef TINY_print
详细参阅:《C++模板元编程》附录。
- BOOST_PP_REPEAT(count, macro, data) 表示一个快速水平重复结构。
count: 重复调用 macro 的次数。而macro(z, n, data) 是三元操作
该宏展开为序列: 传给 macro 的 z 值代表下一个可用的迭代维度。
纵向迭代
局部迭代和文件迭代
- 局部迭代是指借助于BOOST_PPLOCAL…的三个宏,只是简单的完成可REPEAT的功能,没有任何新意。
- 文件迭代是可以将定义放在一个头文件,而且定义不需要用之前的
#define TINY_size(z, n, unused) \ …
来写,可以直接写上迭代的内部部分,而迭代的序号n可以通过BOOST_PP_ITERATION()来取得。然后借助于BOOST_PP_ITERATE等宏来方便的处理。尽管文件迭代非常不错,不过它的迭代内容是定义在另外一个头文件的,因此不够精炼,幸好,preprocessor帮助提供了自迭代,可以在同一个头文件中,分别定义主体和迭代部分。
自迭代
关键的要素:
- 分清楚头文件主体和迭代部分
- 使用宏 BOOST_PP_IS_ITERATING 来区分是否处于迭代状态
- 在主体部分使用BOOST_PP_ITERATION_PARAMS_1和BOOST_PP_ITERATE来完成迭代的指令。
例1:
// 以下内容是头文件 tiny_size.h
#ifndef BOOST_PP_IS_ITERATING // 或者#if !BOOST_PP_IS_ITERATING
// 主体部分
#ifndef TINY_SIZE_H
#define TINY_SIZE_H
#include <boost/preprocessor.hpp>
#include <boost/mpl/int.hpp>
#ifndef TINY_MAX_SIZE
#define TINY_MAX_SIZE 5
#endif
struct none {};
// 定义类模板
template< BOOST_PP_ENUM_PARAMS( TINY_MAX_SIZE, typename T ) >
struct tiny_size : mpl::int_<TINY_MAX_SIZE>
{};
// 自迭代指令,注意指定的头文件是自己的头文件名
#define BOOST_PP_ITERATION_PARAMS_1 (3, (0, TINY_MAX_SIZE-1, "tiny_size.h"))
#include BOOST_PP_ITERATE()
#endif//TINY_SIZE_H
#else
// 迭代部分
#define n BOOST_PP_ITERATION()
#define Tiny_print( z, x, data ) data
// 模板特化定义,注意没有使用#define,不需要’\’,而迭代序号n通过BOOST_PP_ITERATION得到
template< BOOST_PP_ENUM_PARAMS( n, typename T ) >
struct tiny_size<
BOOST_PP_ENUM_PARAMS( n, T )
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM( BOOST_PP_SUB(TINY_MAX_SIZE, n), Tiny_print, none )
>
: mpl::int_<n>
{};
#undef Tiny_print
#undef n
#endif
例2:
另外一个例子,写了一个模仿function的fun
* 支持funN< R, A1, …, AN >
* 支持 fun< R( A1,A2,…,AN ) >
// 头文件(必须)是 func.h ,因为使用了自迭代
// 只要包含该头文件就可以了
#ifndef BOOST_PP_IS_ITERATING // 或者#if !BOOST_PP_IS_ITERATING
// 主体部分
#ifndef FUNC_H
#define FUNC_H
#include <boost/preprocessor.hpp>
#include <boost/function.hpp>
using boost::function;
#define ARGU_SIZE 15
// 主体Repeat宏生成模板类:fun0, fun1, ..., funN-1
// template< typename R, typename ARG0 >
// struct func1
// {
// func1( function<R(ARG0)> const& f )
// : f_(f) {}
// R operator()( ARG0 const& arg0 )
// {
// return f_(arg0);
// }
// private:
// function<R(ARG0)> f_;
// };
#define func_n( z, n, data ) \
template< typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename ARG) > \
struct BOOST_PP_CAT( fun, n ) \
{ \
BOOST_PP_CAT( fun, n )( function<R BOOST_PP_LPAREN() BOOST_PP_ENUM_PARAMS(n, ARG) BOOST_PP_RPAREN() > const& f ) \
: f_(f) {} \
\
R operator() ( BOOST_PP_ENUM_BINARY_PARAMS( n, ARG, const& arg ) ) const \
{ \
return f_( BOOST_PP_ENUM_PARAMS( n, arg ) ); \
} \
\
private: \
function<R BOOST_PP_LPAREN() BOOST_PP_ENUM_PARAMS(n, ARG) BOOST_PP_RPAREN() > f_; \
};
BOOST_PP_REPEAT( ARGU_SIZE, func_n, ~ )
#undef func_n
template< typename Signature> class fun; // 声明,在迭代部分做特化
#define BOOST_PP_ITERATION_PARAMS_1 (3, (0, ARGU_SIZE-1, "func.h"))
#include BOOST_PP_ITERATE()
#endif//FUNC_H
#else
// 迭代部分
// 以下迭代部分生成特化:fun<R()>, fun<R(ARG0)>, ..., fun<R(ARG0, ARG1, ..., ARGN-1)>
// template<typename R, typename ARG1>
// class fun< R(ARG1) >
// : public fun1<R,ARG1>
// {
// public:
// fun( function<R(ARG1)> const& f )
// : fun1<R,ARG1>(f)
// {}
// };
#define n BOOST_PP_ITERATION()
template<typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename ARG) >
class fun< R BOOST_PP_LPAREN() BOOST_PP_ENUM_PARAMS(n, ARG) BOOST_PP_RPAREN() >
: public BOOST_PP_CAT( fun, n )<typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, ARG) >
{
public:
fun( function<R BOOST_PP_LPAREN() BOOST_PP_ENUM_PARAMS(n, ARG) BOOST_PP_RPAREN() > const& f )
: BOOST_PP_CAT( fun, n )<typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, ARG) >( f )
{}
};
#undef n
#endif