预处理元编程

《C++11/14高级编程:Boost程序库探秘》笔记

预处理是从C/C++源码生成最终二进制代码过程中的一个重要步骤,依据简单的规则做文本替换,把文本形式的源码转换为另外一种更适合编译器处理的形式。
C/C++标准定义的预处理功能过于简单随意,boost.preprocessor库创立了一个比较完整的预处理元编程体系,可以在预处理阶段计算整数、执行函数,甚至还有数组、链表等高级结构,能完成一些复杂的任务。
preprocessor库完全由头文件组成,可以直接包含头文件独立使用:
#include <boost/preprocessor.hpp>

小tips:使用选项“-P -E”可以让GCC编译器只执行预处理动作,也就是运行预处理元程序,例如:
gcc -P -E -o a.out basic.cpp; //预处理basic.cpp,输出为a.out

概述

  • 元数据
    与模板元编程相似,预处理元编程领域也有元数据的概念,也是不可变的,但要比模板元编程简单很多,这里没有”类型“,预处理元编程可以识别两种基本数据:

    • 整数:可以有正负号和0x/L/LL修饰
    • 字符串:使用引号(”和”“)包围的字符序列,和C/C++不同,单引号在预处理程序里定义的也是字符串

    此外,另一大类元数据就是”标记“(token),相当于C/C++程序的变量。

  • 基本语法
    预处理指令都以符号”#“开始,比较常用的有以下几个:
#                   //空预处理指令,相当于空行
#include            //引入一个文件
#define             //定义一个标记,用来声明元数据和元函数
#undef              //删除一个标记
#if/#else/#endif    //分支语句,只能识别整数表达式和defined表达式
#ifdef/#ifndef      //检查标记是否已经被定义
#error              //产生一条错误信息,停止预处理
  • 特殊符号
    预处理有三个字符对于元函数有特殊含义,逗号”,”和左右圆括号”(“”)”,逗号用于分隔函数参数,圆括号标记了参数列表,未来能够使用这三个特殊字符,preprocessor库定义了三个专用的元函数来表示它们:
#define BOOST_PP_COMMA() ,      //间接表示逗号
#define BOOST_PP_LPAREN() (     //间接表示左括号
#define BOOST_PP_RPAREN() )     //间接表示右括号

#define BOOST_PP_EMPTY()        //空元函数,无标记
  • 特殊操作符
    在元函数里,符号”#“可以操作标记。
#x      //字符串化,把标记转换为C/C++里的字符串类型
x##y    //标记粘贴(拼接),把两个标记拼接成一个新的标记,即xy

“#”和“##”操作符只能提供基本的字符串化和标记粘贴功能,不能处理元数据,所以preprocessor库提供了两个元函数:BOOST_PP_STRINGIZE(text)和BOOST_PP_CAT(x,y),它们封装了“#”和“##”,可以正确处理元数据。
对比:

#define data1 vector            //定义元数据
#define data2 boost::factory

#define op1(x) #x
#define op2(x,y,z) x##y##z

op1(data1)          //展开为"data1"
op2(data1,data2,z)  //展开为data1data2z

#define op1(x) BOOST_PP_STRINGIZE(x)
#define op2(x,y,z) BOOST_PP_CAT(x,\
                    BOOST_PP_CAT(y,z))

op1(data1)          //展开为"vector"
op2(data1,data2,z)  //展开为vectorboost::factoryz

整数运算

预处理程序识别整数只能用在#if/#endif里对整数进行运算,不能用于元函数,元函数里的表达式会被认为是普通的标记,例如:

#define calc(x,y) int x = y     //定义元函数
calc(x,1+2)                     //调用元函数,展开为int x = 1+2

preprocessor库为此实现了一整套整数运算的元函数,包括以下几类。

  • ADD/SUB/MUL/DIV/MOD :加减乘除和取余
  • INC/DEC :加减1运算
  • MIN/MAX :取较小或较大值
  • AND/OR/NOT/XOR :逻辑运算,与/或/非/异或
  • BITAND/BITOR/BITXOR :位运算
  • BOOL : bool运算,转换为bool值0或1
  • EQUAL/LESS/GREATER:比较运算

这些元函数不能处理负数,正整数也不能超过宏BOOST_PP_LIMIT_MAG的上限(256)

#define x 1
#define y 2

#define v BOOST_PP_ADD(x,y)             //加法,v=3
#define u BOOST_PP_SUB(v,x)             //减法,u=2
#define w BOOST_PP_INC(BOOST_PP_INC(u)) //连续加1,w=4

#if BOOST_PP_BOOL(w)
#define a BOOST_PP_MOD(10,4)
#define b BOOST_PP_MUL(a,300)           //乘法,超过整数上限,出错
#endif

常用元函数

1.ASSERT
元函数BOOST_PP_ASSERT()提供基本的断言功能,类似C/C++里的assert,形式:
BOOST_PP_ASSERT(cond) //cond是判断条件

2.IF
元函数BOOST_PP_IF()实现条件控制语句,类似C/C++里的if,形式是:
BOOST_PP_IF(cond,t,f) //cond是判断条件,cond为0或者为定义,那么结果f,否则t

3.ENUM
元函数BOOST_PP_ENUM()内部用到了BOOST_PP_COMMA_IF,可以生成一个以逗号分隔的标记序列,形式是:
BOOST_PP_ENUM(count,helper,data) //连续调用多次helper

  • count :调用helper的次数,必须是整数且不超过256
  • helper:形如helper(z,n,data)的元函数,被BOOST_PP_ENUM调用
  • data :任意元数据,作为参数传递给helper元函数

元函数BOOST_PP_ENUM()在执行后会展开为:
helper(z,0,data),helper(z,1,data),…,helper(z,count-1,data)
第一个参数z没有实际意义,被BOOST_PP_ENUM()用于迭代,主要利用n和data这两个参数进行运算。

//定义辅助函数,拼接d和n,即d##n
#define helper(z,n,d) BOOST_PP_CAT(d,n)

//定义元函数,通过BOOST_PP_ENUM多次调用helper
#define DECL_VARS(n,val) BOOST_PP_ENUM(n,helper,var)

//调用元函数,展开为"int x0,x1,x2,x3,x4,x5,x6,x7,x8,x9;"
int DECL_VARS(10, x);

4.REPEAT
元函数BOOST_PP_REPEAT()类似BOOST_PP_ENUM(),同样可以生成重复的标记序列,但它的结果没有用逗号分隔,形式是:
BOOST_PP_REPEAT(count,helper,data)
执行后会展开为:
helper(z,0,data)helper(z,1,data)…helper(z,count-1,data)

//定义辅助函数,产生变量声明语句
#define helper(z,n,d) d##n{n};

//定义元函数,通过BOOST_PP_REPEAT多次调用helper
#define DECL_VARS(n,decl) BOOST_PP_REPEAT(n,helper,decl)

//调用元函数,展开为"int x0{0};int x1{1}; int x2{2};"
DECL_VARS(3, int x)

高级数据结构

preprocessor库还支持几种高级数据结构,包括sequence、tuple、array和list。其中,sequence类似C++里的vector,是最容易使用的一种数据结构,它是一串以圆括号包围的标记,例如:

#define seq1 (a)(b)(c)(d)(e)    //包含5个元素
#define seq2 (~123)([] )(--{}--)//包含3个元素

可以对它施加很多操作,例如取长度、拼接、遍历等。

  • SIZE(s) :获取序列的长度,即元素数量
  • HEAD(s) :获取序列的第一个元素
  • TALL(s):获取序列最后一个元素
  • ELEM(n,s):获取序列的第n个元素
  • CAT(s):拼接整个序列
  • FOR_EACH(f,x,s):遍历序列,对每个元素执行函数f(r,x,e),类似REPEAT
#define seq1    (a)(b)(c)(d)(e) //包含5个元素
#define seq2    (~123)([])(--{}--) //包含3个元素

BOOST_PP_SEQ_SIZE(seq1)
BOOST_PP_SEQ_CAT(seq1)

int BOOST_PP_SEQ_HEAD(seq1);

#define helper(r,d,e) BOOST_PP_STRINGIZE(e)

//遍历序列,其中的~参数不适用,宏最后展开为"~123""[]""--{}--"
BOOST_PP_SEQ_FOR_EACH(helper,~,seq2)

猜你喜欢

转载自blog.csdn.net/zuolj/article/details/78520815