`
61party
  • 浏览: 1136082 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

《C++ Template Metaprogramming》附录A——预处理元编程

阅读更多

C++ Template Metaprogramming

附录A:预处理元编程

By David Abraham

(http://www.boost.org/people/dave_abrahams.htm)

By 刘未鹏(pongba)

C++的罗浮宫(http://blog.csdn.net/pongba)

原文链接(http://www.boost-consulting.com/mplbook)

模板元编程是C++中最高阶最抽象,也是最强大的能力。而本章所讲的预处理元编程则与模板元编程相映成趣,相信很多人都没有意识到C/C++预处理器还有这种能力,就像很多人在刚学习C++模板的时候做梦都没有想到还有那么多的衍生技术一样...

动机

即便有模板元编程和Boost Metaprogramming Library的强大的能力可供我们支配,一些C++编程任务仍然需要大量地重复样板式的代码。第五章的tiny_size就是个例子:

template <class T0, class T1, class T2>

struct tiny_size

: int_<3> {};

抛开上面的主模板的模板参数列表中的重复模式[1]不管,下面还有三个偏特化版本,它们更是遵循一个可预期的模式:

template <class T0, class T1>

struct tiny_size<T0,T1,none>

: mpl::int_<2> {};

template <class T0>

struct tiny_size<T0,none,none>

: mpl::int_<1> {};

template <>

struct tiny_size<none,none,none>

: mpl::int_<0> {};

在这种情况下,我们的代码中只有很小一部分具有这种机械味,而我们却要为此重复其余的代码[2],在某些情况下(例如,如果我们实现的是large而非tiny),其余的代码量可能相当可观。当一个模式重复出现两三次或更多时,手动书写就容易导致错误。或许更重要的是,代码会变得难以阅读,因为代码中重要的抽象部分其实正是那个模式,而并非遵循该模式的每个代码片断。

代码生成(Code Generation)

抛开手写吧!机械的代码应该(也的确可以)被机械地生成。对于库的作者,写一个可以生成遵循特定模式的代码片断的程序,然后面临两种选择:一是直接将预生成的源代码文件随库发布,二是发布生成器本身。两者都有缺点。如果客户只得到了预生成的源代码,那么他们就被限制住了——经验表明,这些预生成的代码片断的数目可能今天够用,而明天就不够了!另一方面,如果客户得到了生成程序,那么他们还需要一个可以用来执行该生成程序的程序(例如,解释器),并且,后者必须被整合到build过程中去,除非...

预处理器

...除非生成程序就是预处理元程序[3]!而执行(解释)该元程序的就是C/C++预处理器,尽管它们并非为此目的而设计。用户可以通过#define(代码中)或-D(编译命令行中)来控制代码生成过程。这就避免了上面提到的修改build过程的问题,因为预处理元程序的解释器就是预处理器!例如,我们可以将上面的tiny_size主模板参数化如下:

#include <boost/preprocessor/repetition/enum_params.hpp>

#ifndef TINY_MAX_SIZE

# define TINY_MAX_SIZE 3 // default maximum size is 3

#endif

template <BOOST_PP_ENUM_PARAMS(TINY_MAX_SIZE, class T)>

struct tiny_size

: mpl::int_<TINY_MAX_SIZE>

{};

要测试这个元程序,你可以将编译器切换到预处理模式(使用-E选项),同时确保boost的根目录在#include路径里。例如[4]

g++ -P -E -Ipath/to/boost_1_32_0 -<place w:st="on">I.</place> test.cpp

有了适当的元程序,我们不但可以调整tiny_size的模板参数的个数,还可以调整tiny的最大尺寸——只要#define TINY_MAX_SIZE为恰当的值即可。

Boost Preprocessor Library[MK04]在预处理元编程中充当的角色与MPL在模板元编程中充当的角色类似。它提供了一个高阶构件的framework(例如,BOOST_PP_ENUM_PARAMS),使元编程任务变得容易完成——如果没有这个framework,元编程可能会令人很痛苦:-( 在这个附录中,我们并不去深究预处理器工作的细节或是预处理元编程的一般原则或是BPL库工作的若干细节,而是在一个较高的层次上为你展示这个库,从而让你能够有效地使用它,并且自己探索剩下的部分。

预处理器的基本概念

我们在第二章开始讨论模板元编程——描述了元数据(潜在的模板实参)和元函数(类模板),并在这两个基本概念的基础上构成了对编译期计算的大局观。在本节,我们将以同样的方式来介绍预处理元编程。

这里我们介绍的可能对于你只是一个复习,但是在继续之前,有必要先重申这些基本概念:

预处理标记(Token)

对于预处理器,数据的最基本单元就是预处理标记。预处理标记与你在C++中使用的标识符(identifier),操作符(operator symbol),字面常量(literal)等标记大致对应。从技术上说,预处理标记和正规的标记是有区别的(其细节见C++标准的section 2),但是就目前我们的讨论来说可以暂且忽略。事实上,这里对它们将不作区分。

宏(Macros

宏有两种风格。一种和对象类似:

#define identifier replacement-list

这里identifier是宏的名字,replacement-list是一个或多个tokens的序列。在后面的程序中所有出现identifier的地方都会被展开为replacement-list

另一种是函数风格的宏,它就好比预处理期的元函数

#define identifier(a1, a2, ... an) replacement-list

这里,每一个ai 都代表一个宏形参(parameter)的名字。如果后面的程序中用到了该宏,并给出了适当的实参(argument),那么它将被扩展为replacement-list——其中每次出现宏形参的地方都会被替换为用户给出的宏实参[5]

宏实参(Argument)

定义

宏实参是以下两种预处理标记的非空序列:

1.除逗号或圆括号之外的预处理标记

2.由一对圆括号包围的一集预处理标记

这个定义对预处理元编程有重要影响。注意,首先,下面的两种tokens是特别的:

, ( )

因此,一个宏实参不能包含没有配对的圆括号,或者没有被圆括号包围的逗号。例如,下面的示例代码中,FOO的定义后面的两行代码都是ill-formed

#define FOO(X) X // Unary identity macro

FOO(,) // un-parenthesized comma or two empty arguments

FOO()) // unmatched parenthesis or missing argument

同时还要注意,下面的几种tokens都不是特殊的——预处理器对大括号,方括号,尖括号的配对一无所知:

{ } [ ] < >

所以,下面两行代码是ill-formed

FOO(std::pair<int, long>) // 被解释为以分隔的两个参数

FOO({ int x = 1, y = 2; return x+y; }) // 同上

而如果加上一对冗余的圆括号包围欲传递的参数,代码就正确了:

FOO((std::pair<int,int>)) // one argument

FOO(({ int x = 1, y = 2; return x+y; })) // one argument

但是,由于逗号的特殊含义,所以在不了解一个宏实参包含多少以逗号分隔的标记序列的情况下[6],是不可以随便去掉圆括号的。如果你写了一个宏,并要让它能够接受包含任意多个逗号的宏实参(类似于C里面的可变长参数列表),那么对于使用该宏的用户来说,有两个选择:

1. 将实参用圆括号包围起来,并将其中逗号分隔的token序列的数目作为另一个参数。

2. 将信息编码到一个预处理期的数据结构中去(本章后面会提到)。

BPL库的结构

深入考察BPL库并非本书的范畴,这里我们将给你深入了解BPL工具:你需要使用BPL的电子文档——BOOST_INSTALL/libs/preprocessor目录下的index.htm

打开后,在浏览器的左边你会看到索引,点击其中的“Headers”链接,你会看到整个库的结构。大多数头文件都根据功能被组织在相同的子目录下。顶层的目录仅仅包含一些通用的宏的头文件以及对应每个子目录的头文件(这种头文件仅仅把相应子目录中的头文件都包含进去,例如,boost/preprocessor/selection.hpp包含了selection子目录下的两个头文件max.hpp,min.hpp)。没有对应任何子目录的头文件则声明了一个与文件名同名的宏(有BOOST_PP前缀)。例如,max.hpp声明了BOOST_PP_MAX宏。

你会注意到,通常一个头文件会声明一个额外的宏,它以_D,_R,_Z为后缀[7]。例如,max.hpp中也声明了BOOST_PP_MAX_D宏。在本章中我们会忽略这些宏。如果你想知道它们为何存在以及是如何优化预处理速度的,可以参考电子文档的Topics一节的reentrancy部分。

BPL库的基本概念

在本节中我们将讨论BPL库的基本概念,并分别给出一些简单的例子。

重复

我们可以使用BOOST_PP_ENUM_PARAMS宏生成class T0,class T1,...,class Tn这种(具有特定模式的)重复代码,这符合横向重复的概念。BPL中还有一个纵向重复的概念,我们会在后面介绍。进行横向重复的宏可以在库的repetition子目录下找到。

横向重复

要使用横向重复生成tiny_size的特化版本,我们可以这样写:

#include <boost/preprocessor/repetition.hpp>

#include <boost/preprocessor/arithmetic/sub.hpp>

#include <boost/preprocessor/punctuation/comma_if.hpp>

#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(TINY_MAX_SIZE,n), TINY_print, none) \

> \

: mpl::int_<n> {};

BOOST_PP_REPEAT(TINY_MAX_SIZE, TINY_size, ~)

#undef TINY_size

#undef TINY_print

代码生成从BOOST_PP_REPEAT开始,BOOST_PP_REPEAT是一个高阶的宏,它会重复调用TINY_size宏,也就是它的第二个参数。它的第一个参数指明了重复的次数。而第三个参数可以是任意的,它会被原封不动的传给被调用的宏,这里是TINY_size,而TINY_size并不使用它,所以传递的“~”可以是任意的[8]

TINY_size宏每次被BOOST_PP_REPEAT调用时都会生成一个tiny_size的不同的特化版本。TINY_size宏接受三个参数:

z和前面提到的_Z宏相关。它仅被用于优化的目的。目前我们可以忽略它。

n表示当前为第几次重复。在每次重复调用TINY_size的过程中,n依次为012...

unused,对于这个例子,在每次重复中都是“~”

通常,BOOST_PP_REPEAT会将用户传给它的参数原封不动的转发给被调用的宏(例如,TINY_size)。因为像TINY_size这样的宏的替换文本有好几行,所以除了最后一行,其它行都以反斜杠“\”结尾。其开头的几行调用了BOOST_PP_ENUM来生成以逗号分隔的(模板参数)列表,所以,TINY_size每次被调用都会生成类似下面的代码[9]

template <class T1, class T2, ... class Tn-1>

<p cla
分享到:
评论

相关推荐

    C++模板元编程(C++ 领域扛鼎之作,荣耀先生倾情翻译,名著名译) 原书名: C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond

    本书主要介绍Traits和类型操纵、深入探索元函数、整型外覆器和操作、序列与迭代器、算法、视图与迭代器适配器、诊断、跨越编译期和运行期边界、...,另外附录部分还介绍了预处理元编程概述、typename和template关键字...

    C++ 标准 ISO 14882-2011

    一般规定(General)、工具组件(Utility components)、对(Pairs)、元组(Tuples)、类模板bitset(Class template bitset)、内存(Memory)、智能指针(Smart pointers)、函数对象(Function objects)、元...

    高比例可再生能源电力系统的调峰成本量化与分摊模型——基于Matlab、Yalmip和Cplex的优化研究

    内容概要:本文探讨了高比例可再生能源接入对电力系统调峰能力的影响,提出了一种基于净负荷波动的调峰成本量化与分摊模型。首先,通过将负荷和可再生能源出力曲线转换为无波动的均值线,构建了无调峰需求的替代场景。接着,建立了含深度调峰和抽水蓄能的调度优化模型,用于计算不同场景下的调峰成本。通过比较有无调峰需求两种场景下的系统调峰成本,确定了单一主体导致的边际调峰成本,并采用Shapley值方法合理分摊调峰成本。研究表明,该模型可以有效反映各主体的调峰成本或贡献,有助于促进可再生能源的消纳和电力系统的稳定运行。 适合人群:从事电力系统规划、运营管理和可再生能源研究的专业人士,以及关注能源政策和技术发展的研究人员。 使用场景及目标:适用于评估和优化高比例可再生能源接入条件下的电力系统调峰成本,旨在提高电力系统的灵活性和经济性,同时促进可再生能源的有效利用。 其他说明:该模型需要根据实际情况进行调整和优化,以适应不同地区的电力市场特点和技术水平。

    ABB机器人与博图V16 Profinet通讯及外部启动配置详解

    内容概要:本文详细介绍了如何使用博图V16进行ABB机器人的外部启动及其与西门子设备的Profinet通讯配置。首先概述了ABB机器人和博图V16的基本概念,接着深入讲解了外部启动的重要性和实现方式,重点介绍了FB功能块的应用,以及Profinet通讯的具体配置步骤。文中还强调了GSD文件的作用,用于描述机器人的属性和行为,最后讨论了硬件配置的要求和注意事项,特别是对dsqc1030或dsqc652板卡的支持和888-2或888-3选项的需求。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些负责机器人集成和编程的专业人士。 使用场景及目标:适用于需要将ABB机器人与西门子设备通过Profinet网络进行通讯并实现外部启动的项目。目标是提高自动化生产线的灵活性和效率,确保机器人和PLC之间的无缝协作。 其他说明:本文不仅提供了理论指导,还包含了实际操作中的关键细节,有助于读者快速掌握相关技能并在实践中应用。

    scratch少儿编程逻辑思维游戏源码-3D环境.zip

    scratch少儿编程逻辑思维游戏源码-3D环境.zip

    少儿编程scratch项目源代码文件案例素材-泼溅猫.zip

    少儿编程scratch项目源代码文件案例素材-泼溅猫.zip

    新能源领域基于EMD-ARMA的风光出力预测方法及其应用

    内容概要:本文介绍了基于EMD-ARMA的组合风光出力预测方法,详细阐述了经验模态分解(EMD)和自回归移动平均(ARMA)模型的应用步骤。首先,通过EMD将原始发电数据分解为多个本征模态函数(IMF),然后用ARMA模型对各IMF分量进行建模和预测,最后将预测结果叠加重构,获得最终的风光功率预测值。文中还提供了简化的Python代码示例,帮助读者理解和实现该方法。 适合人群:从事新能源研究和技术开发的专业人士,尤其是对风光发电预测感兴趣的科研人员和工程师。 使用场景及目标:适用于需要提高风光发电预测精度的项目,旨在通过先进的数学模型优化电力调度和资源配置。 其他说明:本文提供的代码示例仅用于教学目的,实际应用中需根据具体情况调整和完善。此外,建议在实践中参考更多专业文献和寻求专家意见以确保预测模型的准确性和可靠性。

    scratch少儿编程逻辑思维游戏源码-scratch RPG 战斗.zip

    scratch少儿编程逻辑思维游戏源码-scratch RPG 战斗.zip

    scratch少儿编程逻辑思维游戏源码-窗户冒险.zip

    scratch少儿编程逻辑思维游戏源码-窗户冒险.zip

    scratch少儿编程逻辑思维游戏源码-FC经典游戏 沙罗曼蛇.zip

    scratch少儿编程逻辑思维游戏源码-FC经典游戏 沙罗曼蛇.zip

    少儿编程scratch项目源代码文件案例素材-跑酷版《我的世界》.zip

    少儿编程scratch项目源代码文件案例素材-跑酷版《我的世界》.zip

    scratch少儿编程逻辑思维游戏源码-抜刀.zip

    scratch少儿编程逻辑思维游戏源码-抜刀.zip

    永磁同步电机无传感器控制:基于反电动势估计的扰动观测器设计与应用

    内容概要:本文介绍了永磁同步电机(PMSM)无位置传感器控制的一种创新方法,重点探讨了通过反电动势估计和扰动观测器增益设计来实现转子位置的精确估算。该方法避免了传统的PLL等位置观测器,仅需一次反正切计算即可获得转子位置,极大简化了系统复杂度。此外,模型控制器采用离散域设计,便于参数调整和适应不同电机参数。文中还提供了具体的Python代码示例,展示了从初始化电机参数到主循环控制的具体实现步骤。 适合人群:从事电机控制系统设计的研究人员和技术工程师,尤其是关注永磁同步电机无传感器控制领域的专业人士。 使用场景及目标:适用于需要简化调试流程、提高系统灵活性和适应多种电机参数的应用场景。主要目标是在保持高性能的同时降低硬件成本和系统复杂性。 其他说明:该方法不仅简化了调试过程,还提高了系统的鲁棒性和可靠性,特别适合于工业自动化、机器人技术和电动汽车等领域。

    汽车制动系统中双腔制动主缸的精细化建模与Simulink-Amesim联合仿真验证

    内容概要:本文深入探讨了乘用车双腔制动主缸的精细化建模及其在Simulink和Amesim中的联合仿真验证。文章首先介绍了双腔制动主缸的物理结构和动力学方程,特别是考虑了液压特性和机械传动的耦合关系。接着,作者详细描述了如何在Simulink中实现这些模型,并通过S函数处理变步长积分问题,确保仿真精度。此外,还讨论了联合仿真过程中遇到的数据交换频率问题,并提出了使用二阶保持器来补偿相位滞后的解决方案。最终,通过对不同推杆力输入条件下的仿真结果对比,验证了精细化模型的有效性和稳定性。 适合人群:从事汽车制动系统研究的技术人员、高校相关专业师生、对车辆动力学仿真感兴趣的工程师。 使用场景及目标:①帮助研究人员更好地理解和掌握双腔制动主缸的工作原理;②为后续更复杂的整车制动系统仿真提供可靠的子系统模型;③提高仿真精度,减少因模型简化带来的误差。 其他说明:文中提供了详细的建模步骤、公式推导、代码实现以及仿真结果对比,附带完整视频教程和参考资料,便于初学者学习。同时强调了实际应用中需要注意的关键细节,如流量计算、数据交换频率调整等。

    scratch少儿编程逻辑思维游戏源码-Scratch版Windows11.zip

    scratch少儿编程逻辑思维游戏源码-Scratch版Windows11.zip

    少儿编程scratch项目源代码文件案例素材-青蛙.zip

    少儿编程scratch项目源代码文件案例素材-青蛙.zip

    基于Matlab Simulink的光伏交直流混合微电网离网模式双下垂控制仿真模型研究

    内容概要:本文详细介绍了光伏交直流混合微电网在离网(孤岛)模式下的双下垂控制仿真模型。该模型利用Matlab/Simulink工具进行构建和仿真,涵盖了直流微电网、交流微电网以及互联变换器(ILC)的结构和控制策略。直流微电网采用电压电流双闭环下垂控制,交流微电网则通过恒压控制和下垂控制来维持稳定的频率和电压。ILC采用双下垂控制策略,通过归一化处理和偏差调整,使得交流母线频率和直流母线电压趋于一致。此外,模型还包括采样保持、坐标变换、功率滤波、SVPWM等辅助环节,以确保系统的稳定运行和高效能量管理。实验结果显示,在负载突增的情况下,系统依然能够保持良好的波形质量和稳定性。 适合人群:对微电网控制系统感兴趣的科研人员、电力工程技术人员及高校师生。 使用场景及目标:适用于研究和验证光伏交直流混合微电网在离网模式下的控制策略,特别是双下垂控制的应用效果。目标是提升微电网的稳定性和能量管理效率。 其他说明:仿真环境为Matlab2020b及以上版本,部分模块仅支持高版本软件。对于希望深入了解双下垂控制机制的研究者,可以通过进一步的学习和交流获得更多信息。

    基于EKF的INS-GPS松组合导航:15状态NED坐标系下的精准定位技术

    内容概要:本文详细介绍了基于扩展卡尔曼滤波器(EKF)的INS(惯性测量单元)和GPS(全球定位系统)松组合导航技术。首先解释了为何需要松组合导航,即通过融合INS和GPS的优势,提高定位的稳定性和准确性。接着阐述了15状态下的EKF融合方法,涵盖速度、姿态、位置等多个系统动态参数的估计与更新。然后讨论了NED(北东地)坐标系的应用及其带来的直观物理意义。最后提供了简化的Python代码片段,演示了如何在EKF中融合INS和GPS数据,以获得连续、稳定的导航结果。 适合人群:从事导航技术研发的专业人士,尤其是对EKF、INS、GPS以及多传感器数据融合感兴趣的工程师和技术研究人员。 使用场景及目标:适用于需要高精度、高可靠性定位系统的应用场景,如自动驾驶汽车、无人机飞行控制系统等。目标是通过融合INS和GPS数据,克服单一传感器的局限性,提升整个导航系统的性能。 其他说明:文中提供的代码仅为概念验证性质,实际工程应用中还需考虑更多复杂的因素和优化措施。

    三相逆变器孤岛运行的双闭环控制策略及LCL滤波电路仿真实现

    内容概要:本文详细介绍了基于MATLAB Simulink平台的三相逆变器稳压控制仿真模型,重点探讨了孤岛运行环境下的电压电流双闭环控制策略及其LCL滤波电路的应用。首先,通过对主电路电流电压的采样并进行Park和Clark变换,将数据转换为dq坐标系下的电流电压值,然后输入双闭环控制系统进行精确调节。接着,通过反变换回到abc坐标系,并利用PWM调制对逆变器进行控制,最终实现了电压电流的稳定输出。文中还提供了简化的Matlab代码片段,展示了关键步骤的具体实现方法。此外,作者通过多次仿真实验验证了该控制策略的有效性和鲁棒性。 适合人群:从事电力电子、自动化控制领域的研究人员和技术人员,尤其是对逆变器控制策略感兴趣的读者。 使用场景及目标:适用于需要深入了解三相逆变器在孤岛运行环境下的稳压控制机制的研究人员和技术人员。目标是掌握电压电流双闭环控制策略以及LCL滤波电路的设计与应用,提高逆变器系统的稳定性和可靠性。 其他说明:本文不仅提供了理论分析,还包括具体的仿真模型和代码示例,有助于读者更好地理解和实践相关技术。

    少儿编程scratch项目源代码文件案例素材-七龙珠RPG 测试.zip

    少儿编程scratch项目源代码文件案例素材-七龙珠RPG 测试.zip

Global site tag (gtag.js) - Google Analytics