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

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

//函数名: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__))\
   )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值