boost::preprocessor库的计算替换

来源:互联网 发布:应届生程序员的薪资 编辑:程序博客网 时间:2024/05/16 17:31

上回说到pp库的第一大类功能是关于宏语言计算的支持。今天就来讲讲这个类型的应用。

首先看个例子。打开你的文本编辑器,输入以下内容,并保存为test.cpp:

#define N 15
#define M (N*(N+1))/2
int m = M;

这段程序用宏设置了一个计算公式:m = (n*(n+1))/2。我们希望经过预编译,它能输出如下的代码:

int m = 120;

现在我们看看实际情况,执行以下命令:

> g++ -P -E test.cpp > test.out.cpp

-E参数指定g++只进行预编译,而-P选项则禁止预编译器产生一些源代码行号等信息,这样会使的我们的输出结果更加清爽。下面看看执行的结果:

int m = (15*(15+1))/2;

看来,这和我们的预期有差别。

宏语言的所有操作依靠一个概念:expansion,它其实是对一些字符串进行展开的操作,计算并不是它的强 项。在这个例子里,预编译只处理了字符串的替换,而并没有进行数值的计算。当然,这样做,我们程序运行的结果还是没有错的,但区别在于,它把计算放到了运 行时,这样就消耗了运行时的计算资源。而我们则希望,在N值相对固定的时候,尽量利用编译时的资源,而节省运行时的CPU资源。这在大规模的计算时还是有 用处的。

boost::preprocessor库则为我们提供了宏计算的功能,而不简单地是字符串的替换,下面的代码引入了boost::preprocessor库:

#include <boost/preprocessor/arithmetic/div.hpp>
#include <boost/preprocessor/arithmetic/mul.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>

#define N 15
#define M BOOST_PP_DIV( /
BOOST_PP_MUL( N, BOOST_PP_INC(N) ), /
2 )
int m = M;

这里我们用到了三个pp库“函数”:DIV, MUL 和 INC,分别对应整数除,乘和加一操作。输入的时候注意不要遗漏行末的’/'符号,因为我们的宏语句必须是同一行。另一个需要注意的是,每个操作都被包含在一个单一的boost::preprocessor库的头文件里,而这个头文件的命名是很有规范的,比如我们的宏函数如果是BOOST_PP_ADD,那么它的头文件就在add.hpp里。这个命名规范也方便我们查阅pp库的在线帮助系统。

插播一下pp库的在线帮助系统。首先从boost.org进入pp库的文档系统,比如目前的1.34.1版本boost的pp库文档位于:
http://boost.org/doc/libs/1_34_1/libs/preprocessor/doc/index.html

在这个页面里,选择左边导航栏的”Reference”,这样就进入了函数查阅页面。这个页面在左边列出了所有的函数名,我们可以非常直观地找到我 们需要查阅的函数,比如查BOOST_PP_ADD的说明,就点击”ADD”条目,这样就进入了该函数的详细说明。养成习惯,尽量使用这个帮助系统,老外 写的文档还是很敬业的:)

好了,同样用g++ -P -E命令对刚才的代码进行预编译,再次查看我们的输出:

int m = 120;

如果你在Windows下测试时,可能会遇到找不到boost库的头文件的问题,请做相应的环境设置,或者简单地用 /I[your boost include dir] 参数来设置。而在linux下,通过诸如ubuntu或者debian这样的包管理安装的boost,所有头文件都按照规范安装到了/usr/include目录下,我们也就不再为环境设置烦心了。

以上的例子是一个非常简单的用法,你也许会说:我不关心是否占用运行时的CPU资源来进行运算。那么我们下面就结合一下pp库的代码重复生成功能来看看计算的功能。

#include <boost/preprocessor/arithmetic/div.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <boost/preprocessor/cat.hpp>

#define N 10

typedef struct data
{
#define BOOST_PP_LOCAL_MACRO(n) /
BOOST_PP_CAT( m, n ) ;
#define BOOST_PP_LOCAL_LIMITS (0,N-1)
#include BOOST_PP_LOCAL_ITERATE()

#define MEMBER(m, n) BOOST_PP_CAT( m, n )

void foo(data& input)
{
#define BOOST_PP_LOCAL_MACRO(n) /
MEMBER(m, n) /
= /
MEMBER( input, BOOST_PP_DEC(BOOST_PP_SUB(N,n)) ) ;
#define BOOST_PP_LOCAL_LIMITS (0,N-1)
#include BOOST_PP_LOCAL_ITERATE()
};
};

注意其中的红色部分,这里用计算来计算变量的索引,而这种替换,是绝计无法放到运行时去执行的,否则连C++的编译都通不过。虽然这里涉及到我们还没讲到的代码重复产生功能,但我们可以根据执行这段代码,检查生成的结果来体会宏计算在这里的重要性。

这段代码将产生如下的代码:

typedef struct data
{
m0 ;
m1 ;
m2 ;
m3 ;
m4 ;
m5 ;
m6 ;
m7 ;
m8 ;
m9 ;
void foo(data& input)
{
m0 = input9 ;
m1 = input8 ;
m2 = input7 ;
m3 = input6 ;
m4 = input5 ;
m5 = input4 ;
m6 = input3 ;
m7 = input2 ;
m8 = input1 ;
m9 = input0 ;
};
};

最后,我们需要注意,pp库的计算还是能力很有限的,基本上,它处理不了超过256的计算,比如,我们测试一下以下的代码:

#include <boost/preprocessor/arithmetic/mul.hpp>
#include <boost/preprocessor/arithmetic/div.hpp>
int m = BOOST_PP_MUL( 128, 3 );
int n = BOOST_PP_DIV( 257, 3 );

通过观察结果,乘法的结果是256,显然这是错误的,而除法的结果则是一长窜难于阅读的错误输出,而其实他们都是由于pp库不支持超过256的计 算。而我们的预编译器并不能报出清晰的错误,所有我们一定要对这些限制有所了解,这就要求我们在使用每个pp函数的时候,请一定仔细阅读在线文档里关于该 函数的说明。

原创粉丝点击