最近的工作中有这样一个需求:
使用宏自动生成类成员函数的声明和实现代码,成员函数的返回值类型不定,参数表可能为空,也可能有任意个任意类型的参数,例如:

//函数名:foo0、返回值:int、参数类型表:空,生成函数:
int foo0(){ return 0;}
//函数名:foo1、返回值:int、参数类型表:int,生成函数:
int foo1(int v0) { return v0+1; }
//函数名:foo2、返回值:int、参数类型表:int, int,生成函数:
int foo2(int v0, int v1) { return foo1(v0) + foo1(v1); }
//函数名foo3、返回值:int、参数类型表:float,生成函数:
int foo3(float v0) { return (int)v0; }
//函数名:foo4、返回值:int、参数类型表:int, int, int,生成函数:
int foo4(float v0, int v1, int v2) { return foo3(v0) + foo2(v1, v2); }
//...

分析上述需求:返回值类型、函数名在宏的参数表中只占固定数目的参数,因此并不难实现;而函数参数表和调用函数时传入的参数列表则因为参数类型表的元素及其个数不定而颇为困难,且只能考虑使用编程语言的变长参数特性。

于是我考察了C/C++语言的各种特性,支持变长参数的有C99的variadic macro和C++11的variadic template,但使用后者来实现似乎更困难些,因为个人感觉template更多的用于对模版特异化并生成对象的实例,而用于直接生成代码的用法似乎并不多见,于是只剩下C99的variadic macro可选了。

C++的Boost库提供了预处理器元编程的支持,并且Boost库至少从1.55版开始便提供了C99关于variadic macro的元编程宏,这使得在宏中根据变长的类型参数生成函数参数表成为可能。

于是经过一周多的工作,终于设计出了一系列可以根据变长的类型列表来生成函数参数表的宏:


#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/comparison/less.hpp>
#include <boost/preprocessor/logical/and.hpp>
#include <boost/preprocessor/logical/nor.hpp>
#include <boost/preprocessor/logical/not.hpp>
#include <boost/preprocessor/logical/or.hpp>
#include <boost/preprocessor/control/expr_if.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/variadic/size.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/facilities/is_empty.hpp>
#include <boost/preprocessor/tuple/to_seq.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/preprocessor/tuple/rem.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#define __RFLX_FIELDOF_EMPTYTYPE_3PARAMS__(p1, p2, p3) BOOST_PP_EMPTY()
#define __RFLX_FIELDOF_EMPTYTYPE_1PARAM__(p) BOOST_PP_EMPTY()
#define __RFLX_PARAOF_UNEMPTYTYPE__(lastidx, i, elem) \elem BOOST_PP_CAT(para_, i)BOOST_PP_COMMA_IF( BOOST_PP_LESS(i, lastidx))
#define __RFLX_PARA_OF_TYPE(r, lastidx, i, elem)\BOOST_PP_IF(BOOST_PP_IS_EMPTY(elem),         \__RFLX_FIELDOF_EMPTYTYPE_3PARAMS__,       \__RFLX_PARAOF_UNEMPTYTYPE__               \)(lastidx, i, elem)
/*!* \macro RFLX_PARALIST_FROM_TYPELIST* \brief 从参数类型列表生成的函数的参数表** \details 本宏用于从形如'int, char const*, float, ...'的参数类型列表生成形如* 'int para_0, char const* para_1, float para_2, ...'的参数表,因此本宏可用* 于根据宿主宏给出的变长参数表生成函数定义中的函数参数表;* 当参数类型列表为空时,本宏生成空的参数表;** \attention* - 在宏中使用时,对由变长参数(...)给出的参数类型表,须使用宏`__VA_ARGS__`来对本宏赋值* - 本宏生成的参数表中的参数名以'para_'接数字的形式命名,数字按参数位置从0开始计数** \param atHead 指示生成的参数表在目标列表中是否处于起始位置,0-非起始位置,其他非0值-起始位置;*               当生成的参数表非空,且未处于起始位置时(atHead为0),将在生成的参数表前添加一个逗*               号以串接在前面参数表的尾部* \param ...    给定的参数类型列表** \example* ```cpp* #define MACRO_FOO(fnName, ...)   fnName(RFLX_PARALIST_FROM_TYPELIST(1, __VA_ARGS__))* MACRO_FOO(foo, double, int, char const*)//生成代码:foo(double para_0, int para_1, char const* para_2)* MACRO_FOO(foo0)                         //生成代码:foo0()** #define MACRO_FOO2(fnName, ...)   \\* fnName(int predefined_para RFLX_PARALIST_FROM_TYPELIST(0, __VA_ARGS__))* MACRO_FOO2(foo, double, int, char const*)* //生成代码:foo(int predefined_para, double para_0, int para_1, char const* para_2)* MACRO_FOO2(foo0)* //生成代码:foo0(int predefined_para)* ```*/
#define RFLX_PARALIST_FROM_TYPELIST(atHead, ...)            \BOOST_PP_COMMA_IF(BOOST_PP_NOR(                          \BOOST_PP_BOOL(atHead), BOOST_PP_IS_EMPTY(__VA_ARGS__) \))BOOST_PP_SEQ_FOR_EACH_I( __RFLX_PARA_OF_TYPE,          \BOOST_PP_DEC(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)),    \BOOST_PP_IF(BOOST_PP_IS_EMPTY(__VA_ARGS__), (),       \BOOST_PP_TUPLE_TO_SEQ(                             \BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),(__VA_ARGS__)\)                                                  \)                                                     \)#define __RFLX_ARGOF_UNEMPTYTYPE__(lastidx, i, elem) \BOOST_PP_CAT(para_, i)BOOST_PP_COMMA_IF( BOOST_PP_LESS(i, lastidx))
#define __RFLX_ARG_OF_TYPE(r, lastidx, i, elem) \BOOST_PP_IF(BOOST_PP_IS_EMPTY(elem),         \__RFLX_FIELDOF_EMPTYTYPE_3PARAMS__,       \__RFLX_ARGOF_UNEMPTYTYPE__                \)(lastidx, i, elem)
/*!* \macro RFLX_ARGLIST_FROM_TYPELIST* \brief 从参数类型列表生成函数的参数传递表* \details 本宏用于从形如'int, char const*, float, ...'的参数类型列表生成形如* 'para_0, para_1, para_2, ...'的参数表,因此本宏可用于根据宿主宏给出的变长参数* 表生成函数调用时的参数传递表;* 当参数类型列表为空时,本宏生成空的参数传递表。* \attention* - 在宏中使用时,对由变长参数(...)给出的参数类型表,须使用宏`__VA_ARGS__`来对本宏赋值* - 本宏生成的参数表中的参数名以'para_'接数字的形式命名,数字按参数位置从0开始计数,因此* 本宏须与 #RFLX_PARALIST_FROM_TYPELIST 配合使用** \param atHead 指示生成的参数传递表在目标列表中是否处于起始位置,0-非起始位置,其他非0值-起始位置;*               当生成的参数传递表非空,且未处于起始位置时(atHead为0),将在生成的参数传递表前添加一*               个逗号以串接在前面参数传递表的尾部* \param ...    给定的参数类型列表** \example* ```cpp* #define MACRO_FOO(fnName, ...)   \\* fnName(RFLX_ARGLIST_FROM_TYPELIST(1, __VA_ARGS__))* MACRO_FOO(foo, double, int, char const*)//生成代码:foo(para_0, para_1, para_2)* MACRO_FOO(foo0)                         //生成代码:foo0()** #define MACRO_FOO2(fnName, ...)   \\* fnName(predefined_para RFLX_ARGLIST_FROM_TYPELIST(0, __VA_ARGS__))* MACRO_FOO2(foo, double, int, char const*)* //生成代码:foo(predefined_para, para_0, para_1, para_2)* MACRO_FOO2(foo0)//生成代码:foo0(predefined_para)* ```*/
#define RFLX_ARGLIST_FROM_TYPELIST(atHead, ...)                               \BOOST_PP_COMMA_IF(BOOST_PP_NOR(atHead, BOOST_PP_IS_EMPTY(__VA_ARGS__)))    \BOOST_PP_SEQ_FOR_EACH_I(__RFLX_ARG_OF_TYPE,                                \BOOST_PP_DEC(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)),                      \BOOST_PP_TUPLE_TO_SEQ(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),(__VA_ARGS__))\)

使用基于Boost的预处理器元编程实现变长类型列表的参数化相关推荐

  1. 跟我学c++高级篇——模板元编程之十一鸭子类型

    一.鸭子类型 鸭子类型不是从c++中出现的,duck typing这种称呼在Python中比较多见.那么什么是鸭子类型呢?它是动态类型的一种风格,只要是对象的特征(其方法和属性集)和某个类型一致,就认 ...

  2. 【C++ 泛型编程 进阶篇】:用std::integral_constant和std::is_*系列深入理解模板元编程

    C++ 元模版编程:用std::integral_constant和std::is_*系列深入理解模板元编程 一.模板元编程与类型特性 (Template Metaprogramming and Ty ...

  3. C 语言编程 — 宏定义与预处理器指令

    目录 文章目录 目录 前文列表 宏 预处理器 预处理器指令 预处理器指令示例 预处理器指令运算符 宏定义 简单宏定义 带参数的宏定义 符号吞噬问题 使用 do{}while(0) 结构 预定义的宏 常 ...

  4. python预处理c语言_详解C语言编程中预处理器的用法

    预处理最大的标志便是大写,虽然这不是标准,但请你在使用的时候大写,为了自己,也为了后人. 预处理器在一般看来,用得最多的还是宏,这里总结一下预处理器的用法. #include #define MACR ...

  5. 基于C++11模板元编程实现Scheme中的list及相关函数式编程接口

    前言 本文将介绍如何使用C++11模板元编程实现Scheme中的list及相关函数式编程接口,如list,cons,car,cdr,length,is_empty,reverse,append,map ...

  6. boost::mpl元编程之高阶元函数演示

    所谓元编程就是在编译期能够做的一些计算在编译期做,等到运行时就不用做了. 所以说C++牛比之处就在这儿,程序还没运行在编译期就已经知道结果了. 元编程一般用于动态确定某一种类型. 例如根据条件确定使用 ...

  7. 第12章 元编程与注解、反射

    第12章 元编程与注解.反射 反射(Reflection)是在运行时获取类的函数(方法).属性.父类.接口.注解元数据.泛型信息等类的内部信息的机制.这些信息我们称之为 RTTI(Run-Time T ...

  8. (转载)虚幻引擎3--9掌握虚幻技术UnrealScript 预处理器

    第九章 – UNREALSCRIPT预处理器 9.1概述 9.2 MACRO(宏)的基础知识 指南 9.1 –您的第一个宏 9.3具有参数的宏 指南 9.2 –       MACRO参数 9.4内置 ...

  9. 【Groovy】Groovy 语言特点简介 ( 支持 Java 语法 | 支持 Java 虚拟机 | Groovy 语言是动态语言 | Groovy 扩展 JDK | 编译时元编程 )

    文章目录 一.Groovy 支持 Java 语法 二.Groovy 支持 Java 虚拟机 三.Groovy 语言是 动态语言 四.Groovy 扩展 JDK 五.Groovy 编译时元编程 一.Gr ...

最新文章

  1. PHP创建XML文件讲解
  2. 2018年6月2号(线段树(2))
  3. PayPal宣布退出虚拟货币组织Libra协会
  4. Tomcat 6连接池配置
  5. 2022中国私域流量管理研究报告
  6. bootstrap下载地址
  7. 一张图学会python3高清图-用一张很丑的图学习Python数据可视化基础--热力图
  8. 25. 熟悉非标准的哈希容器
  9. EasyUI下拉框自适应高度
  10. 电脑网络连接里多了个计算机,怎么样把几台电脑连接在同一个局域网
  11. ipad+PDF Expert:买前生产力,买后生产力
  12. 小象学院python网课值得吗-小象学院的机器学习集训营课程怎么样?
  13. ssh开启图形界面_分享|3 个 Linux 上的 SSH 图形界面工具
  14. 健身房训练计划—背部
  15. iou(交并比)的概念
  16. 每日一题吧算是,见证自己的成长路程_(:з」∠)_
  17. 弹出率很高的网页弹窗代码
  18. 实战演练--保存QQ账号与密码
  19. 微信获取scheme码提示invalid weapp pagepath rid: 6397ef44-0f537d77-76155114
  20. 山东师大计算机专业研究生,山东师范大学现代教育技术(专硕)专业考研难度分析-专业排名-难度大小...

热门文章

  1. 【daisy-framework】MySQL 开发规范
  2. Kaggle信贷预测随笔
  3. python万里长征2(非教)
  4. 用python分析NBA联盟球员信息,才知道这些秘密!
  5. 通过wlanapi连接无线网络
  6. up主迎来春天,开始叫马云爸爸啦,阿里巴巴通过淘宝收购B站2400万股
  7. 三菱四节传送带控制梯形图_三菱PLC用步进指令实现四节传送带的模拟控制.pdf...
  8. [数据库基础篇]——数据库系统概述、数据模型、数据库系统的结构、数据库系统的组成
  9. 只有tcp 没有rtsp_如何在2020年在您的网站上烹饪RTSP,或者为什么公猪没有机会逃脱
  10. BCG 全局主题样式