preprocessor是强大的预处理元编程库,构建了一套预处理期间的数据结构和迭代控制流。

宏的基础知识

宏定义

#define identify replacement_list

定义的identify为宏名,后续文本中出现的identify都会被预处理器替换为对应的replacement_list。

#define identify( a1, a2, …, an ) replacement_list

类似函数的宏,是 “预处理阶段的元函数”。每个ai标识符命名了一个宏形参,当宏名字在后续文本中出现,并且后跟适当的实参列表argument_list,它将被替换为replacement_list,并且其中的参数的位置替换为指定的实参。

宏实参

定义:宏实参列表是以下两种预处理标记(Preprocessing Tokens)的非空序列(逗号分隔):

注意:

例子:

#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));
// ...

分析下,这种写法的弊端:

  1. 手动书写不仅低效,而且容易出错。
  2. 需要扩展很麻烦

考虑,最好让代码来生成这些代码,而这种情况是元编程无能为力的,它已经在编译期之前了,让其他程序来生成代码拷贝进来显然是不大合理的。

boost库自带的preprocessor的参考reference也非常好,详细信息可以查阅,很方便。本文档只是分析了它的核心内容,以及按照重点进行分类,分析。

数据结构抽象及其操作函数

宏实参列表,类似于函数参数列表(arg1, arg2, …, argn),规则如上定义,将合法的实参进行抽象、规范成合理的“数据类型”,将使用预处理的行为更规范、更可控、更丰富。

Tuple元组

tuple 是一个简单的在括号中用逗号分隔的元素列表。例如, (a, b, c) tuple的操作非常简单高效,但是预定义的元组的操作宏都需要指定元组长度。

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(3, (x, y, z)) // 展开为 (x, (y, (z, BOOST_PP_NIL)))

例如:BOOST_PP_TUPLE_TO_SEQ(3, (a, b, c)) // 展开为 (a)(b)©

例如:BOOST_PP_TUPLE_REM(3)(x, y, z) // 展开为 x, y, z

例如:BOOST_PP_TUPLE_REM_CTOR(3, (x, y, z)) // 展开为 x, y, z

例如:BOOST_PP_TUPLE_EAT(2)( 1,2 ) 展开为空

Array数组

数组 是一个包含两元素 tuple 的数据结构。第一个元素是数组中的元素数量。第二个元素是由数组中的元素所组成的 tuple. 例如,(3, (a, b, c)) ,长度为3,元素放在tuple中 数组的最大优势在于保存了自己的大小。因此,访问元素时不需要指定大小。只需要在给定的索引位置上有相应元素就可以了。 使用数组可以使得宏的参数数量可变,同时还允许数据声明改变大小,而无需用户明确知道该大小。

例如:

#define ARRAY (3, (x, y, z))  
BOOST_PP_ARRAY_DATA(ARRAY) // 展开为 (x, y, z)

例如:

#define ARRAY (4, (a, b, c, d))  
BOOST_PP_ARRAY_ELEM(0, ARRAY) // 展开为 a

例如:

#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.。

d: 下一个可用的 BOOST_PP_WHILE 迭代。

该宏返回删除最后一个元素后的数组。如果数组中没有元素,则该宏的结果为未定义。

例如:

#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

z: 下一个可用的 BOOST_PP_REPEAT 维度。

i: 被删除的元素在数组中的位置,从零起计。有效值为 0 到 BOOST_PP_ARRAY_SIZE(array) - 1.

例如:

#define ARRAY (3, (a, b, d))  
BOOST_PP_ARRAY_REMOVE(ARRAY, 2) // 展开为 (2, (a, b))

例如:

#define ARRAY (3, (a, b, c))  
BOOST_PP_ARRAY_REVERSE(ARRAY) // 展开为 (3, (c, b, a))

序列 seq

序列 (简称为 seq)是一组相邻的带括号的元素。例如, (a)(b)©(d) seq 不能为空。因此,一个 “空的” seq 被认为是一种特殊情况,必须由C++单独处理。 对于序列的处理效率是非常高的,可以认为是随机访问的。

例如:

#define SEQ (a)(b)(c)
BOOST_PP_SEQ_CAT(SEQ) // 展开为 abc

要达到最高效率,请使用BOOST_PP_SEQ_CAT_S(s, seq)。

s: 下一个可用的 BOOST_PP_SEQ_FOLD_LEFT 折叠(fold)步骤。

例如:

#define SEQ (B)(O)(O)(S)(T)
BOOST_PP_SEQ_ENUM(SEQ) // 展开为 B, O, O, S, T

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.。

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

从左至右对一个 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

对于 seq, (0)(1)(2), 该宏展开为: op(s, op(s, op(s, state, 2), 1), 0)

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_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.

对一个 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.

对一个 seq 中的每个元素重复调用一个指定的宏。它以最高的效率重入 BOOST_PP_FOR. 该宏是一个迭代结构。

如果 seq 为 (a)(b)©, 则它展开为序列: macro(r, data, 0, a) macro(r, data, 1, b) macro(r, data, 2, c)

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)

对多个 seqs 的每个笛卡尔积重复某个宏。它以最高的效率重入 BOOST_PP_FOR.

该宏是一个工具宏,表示一个添加到尾部的空起始点。它不是一个空的 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)

其他的一些序列操作宏:

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))

链表 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

横向枚举生成

宏展开为一个以逗号分隔的序列: macro(z, 0, data), macro(z, 1, data), … macro(z, count - 1, data)

宏展开为一个以逗号分隔的序列: param ## 0, param ## 1, ... param ## count - 1

宏展开为一个以逗号分隔的序列: p1 ## 0 p2 ## 0, p1 ## 1 p2 ## 1, ... p1 ## count - 1 p2 ## count - 1

宏展开为一个以逗号分隔的序列: param ## 0 = def, param ## 1 = def, ... param ## count - 1 = def

宏展开为一个以逗号分隔的序列: param ## 0 = def ## 0, param ## 1 = def ## 1, ... param ## count - 1 = def ## count - 1

宏展开为一个以逗号分隔的序列: macro(z, 1, data), … macro(z, count - 1, data)

宏展开为一个以逗号分隔的序列: p1 ## 1 p2 ## 1, p1 ## 2 p2 ## 2, ... p1 ## count - 1 p2 ## count - 1

宏展开为一个以逗号分隔的序列: param ## 1, ... param ## count - 1

宏展开为一个以逗号分隔的序列: , macro(z, 0, data), macro(z, 1, data), … macro(z, count - 1, data)

宏展开为一个以逗号分隔的序列: , p1 ## 0 p2 ## 0, p1 ## 1 p2 ## 1, ... p1 ## count - 1 p2 ## count - 1

展开:, param ## 0, param ## 1, ... param ## count - 1

重复repeat

count: 重复调用 macro 的次数。而macro(z, n, data) 是三元操作

该宏展开为序列: macro(z, 0, data) macro(z, 1, data) ... macro(z, count - 1, data) 传给 macro 的 z 值代表下一个可用的迭代维度。

宏展开为序列: macro(z, first, data) macro(z, first + 1, data) … macro(z, last - 1, data)

宏展开为序列: 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)

迭代控制、结构控制

bit: 确定结果为 expr 或为空的条件。该值必须展开为 0 或 1.

该宏不会对其第一个参数执行布尔类型转换。如果需要转换,请改用 BOOST_PP_EXPR_IF

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)停止,不输出)

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)) … ).

其他

它的实现:#define BOOST_PP_IDENTITY(item) item BOOST_PP_EMPTY

如果 x 为 BOOST_PP_NIL, 该宏展开为空。如果 x 为一个单元素的 tuple, 则展开为 tuple 中的内容。

重要的能力展示

横向重复

关键是利用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++模板元编程》附录。

count: 重复调用 macro 的次数。而macro(z, n, data) 是三元操作

该宏展开为序列: 传给 macro 的 z 值代表下一个可用的迭代维度。

纵向迭代

局部迭代和文件迭代

自迭代

关键的要素:

例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