boost::preprocessor库之横向重复与纵向重复
来源:互联网 发布:雨血全剧情知乎 编辑:程序博客网 时间:2024/06/05 22:37
下面我们我们来看看pp库的另一大功能:代码重复产生。代码重复在模板类的特例化里非常有用,那能方便地帮我们产生数量众多的模板特例化。下面看一个简单的特例化模板产生的代码:
#include <boost/preprocessor/repetition/enum_params.hpp>
#define M 3
template <BOOST_PP_ENUM_PARAMS(M, class T)>
struct tiny_size
: mpl::int_<M>
{};
同样用g++ -P -E
进行预编译,查看一下结果:
template < class T0 , class T1 , class T2>
struct tiny_size
: mpl::int_<3>
{};
可以看出,ENUM_PARAMS
是专门用来重复生成参数列表代码的,它能够自动地在参数名称后面为我们加上索引号。这样,我们写出一个特例化模板的样例代码,就可以通过改变M值来方便地随意产生各种特例话模板。
那么我们如果要同时产生多个特例化呢?来看看这个稍微复杂点的例子:
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#define M 3
#define TINY_print(z, n, data) data
#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(M,n), TINY_print, none) /
> : mpl::int_<n> {};
BOOST_PP_REPEAT(M, TINY_size, ~)
#undef TINY_size
#undef TINY_print
首先,我们看到,我们包含了头文件 repetition.hpp
,这个文件里包含了各种横向重复的宏,比如BOOST_PP_ENUM
、BOOST_PP_ENUM_PARAMS
、BOOST_PP_REPEAT
,他们区别在于具体重复的模式,比如ENUM
用来产生逗号分隔的重复,而内容可以由用户自己定义,比如我们这里的TINY_print
;ENUM_PARAMS
则允许生成参数模式的重复;而REPEAT
则只重复,而不产生逗号分隔符。
我们还应该注意到,TINY_print
和TINY_size
这两个宏的参数形式都是(z,n,d)
,其中z用做一些优化,我们可以暂时不用考虑,n则是重复时由pp库传入的当前重复的索引,d则为用户传入的token,如果我们不需要用户传入对象,则可以用符号’~'来占位。
最后,细心的读者也许还要问,为什么print和size这两个宏是小写字母?这是由于我们定义的这些宏将只在这块代码里有效,我们在重复结束后将其undef了,这样我们就不会影响宏的全局命名空间(宏里面可没有namespace这些高档玩意儿)。
COMMA_IF
则根据参数是否不为0,来决定是否产生一个逗号。下面我们看看这段代码的输出:
template <> struct tiny_size < none , none , none > : mpl::int_<0> {}; template < class T0> struct tiny_size < T0 , none , none > : mpl::int_<1> {}; template < class T0 , class T1> struct tiny_size < T0 , T1 , none > : mpl::int_<2> {};
好了,请留下2分钟,仔细根据输出揣摩一下pp库的使用。这其中用到了两层的重复,最外一层使用REPEAT
,每次重复生成一个完整的特例化模板类;第二层有两个重复,一个用ENUM_PARAMS
来生成参数列表,而另一个使用ENUM
来生成占位用的none字符串。
结果是正确了,可是,我们还是对结果非常不爽,因为,输出居然是一整行!首先,这非常不利于代码的阅读;其次,我们的debugger在报错时一般是按照代码的行号来进行报错的,而这样一整行,使得所有的错误都指向同一行!
造成这种不爽的原因在于,我们的宏总是在一行中完成,这使得横向的重复对于函数参数的重复来说非常适用,但对于大范围的重复,比如一个类的定义,则 不太适用了。当然,boost的guru们绝不善罢甘休,他们造出了纵向重复这个概念来解决这个问题。而纵向重复根据不同的实现细节又分成了LOCAL重 复,文件重复和自我文件重复三大类。我们首先看看用LOCAL重复来改善一下上面那段代码。
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#define M 3
#define TINY_print(z, n, data) data
#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(M,n), TINY_print, none) /
> : mpl::int_<n> {};
//BOOST_PP_REPEAT(M, TINY_size, ~)
#define BOOST_PP_LOCAL_MACRO(n) TINY_size(~,n,~)
#define BOOST_PP_LOCAL_LIMITS (0,M-1)
#include BOOST_PP_LOCAL_ITERATE()
#undef TINY_size
#undef TINY_print
LOCAL_ITERATE()
这个函数用来生成纵向重复代码,但它的输入要求有两个特殊的宏的定义:LOCAL_MACRO(n)
和LOCAL_LIMITS
,前着在每次重复的时候调用,传入重复的索引,后者用来定义重复的范围。注意,我们在重复范围定义时,用的是0到M-1,所以,对于LIMITS(a,b),它实际重复的范围是[a,b],这是一个全闭的空间。
好了,简单地进行了这样一个改进,让我们来看看输出的结果:
template <> struct tiny_size < none , none , none > : mpl::int_<0> {};
template < class T0> struct tiny_size < T0 , none , none > : mpl::int_<1> {};
template < class T0 , class T1> struct tiny_size < T0 , T1 , none > : mpl::int_<2> {};
是不是好多了?当然,追求完美的我们还是不满意,毕竟虽然每个模板类被分配在了单独的一行,但每个模板类却还是在同一行。要继续改进,就需要用到文件重复以及自我重复。
- boost::preprocessor库之横向重复与纵向重复
- boost::preprocessor库之文件重复与自我重复
- boost::preprocessor库简介
- 纵向扩展与横向扩展
- 纵向扩展与横向扩展
- 纵向扩展与横向扩展
- 横向越权与纵向越权
- boost::preprocessor库之嵌套循环及其它
- boost::preprocessor库使用入门
- 如何在DW/asp中让重复记录横向[N条记录]超过N条再纵向排
- ASP横向重复显示记录
- 重复横向显示记录集
- 数据库中的横向设计与纵向设计
- html td 的横向与纵向合并
- div的横向与纵向排列
- Rdlc报表纵向与横向打印问题
- div的横向与纵向排列
- Oracle之纵向数据转换横向数据
- 按照字符数截取字符串
- Pointsec MI 和数据安全
- Class Of Marquee Scroll通用不间断滚动JS封装类
- 更改排序规则
- HGE基础教程(1)
- boost::preprocessor库之横向重复与纵向重复
- 简简单单的心得 关于jsp 路径问题
- 简简单单的心得 关于jsp 路径问题
- SQL Server 2000的数据库所有数据参数
- JavaMail之POP3协议判断新邮件的思路
- 双手合十-------新年之开篇
- PHP名词库
- Apache+php+mysql在Linux下的安装与配置
- 关闭窗口的时候不弹出提示直接关闭