【问题标题】:C preprocessor : call function with same argument as parentC预处理器:调用具有与父级相同参数的函数
【发布时间】:2018-04-09 06:27:48
【问题描述】:

我想定义很多函数调度器。基于一个标志,我会调用一个或另一个。标志检查总是一样的,命名也是一样的。

这是一个示例代码:

int myfunction(int a,int b)
{
    if (flag)
        return myfunction_flag1(a, b);
    else
        return myfunction_flag0(a, b);
}

由于此代码将针对我的每个函数重复(实际用例使用的行数比仅此 if else 多,但出于问题目的而对其进行了简化),因此我想将其编写为 MACRO。

#define DISPATCHER(function_type, function_name, ...) \
function_type function_name(__VA_ARGS__)              \
{                                                     \
    if (flag)                                         \
        return function_name ## flag1(__VA_ARGS__);   \
    else                                              \
        return function_name ## flag0(__VA_ARGS__);   \
}                                                     \

然后有很多:

DISPATCHER(int, myfunction, int a, int b)
DISPATCHER(int, myfunction2, int a, int b, int c)
DISPATCHER(int, myfunction3, int a)

...

但是,我不能调用 function_name ## flag1(\__VA_ARGS__),因为 \__VA_ARGS__ 包含参数类型。 有没有办法以另一种方式做到这一点?

【问题讨论】:

  • 还有 2017 年...为什么不用 C++ 呢?
  • 为什么要将它作为宏?为什么不使用内联函数? (相信你的编译器会优化不必要的函数调用)
  • 标志可以在运行时改变。我想向我的用户公开一个干净的功能。然后在后端检查我的标志并调用适当的函数(与原始函数具有相同的签名)。
  • 我相信,如果坚持使用宏,你将不得不遵循类似于here 描述的宏定义,并重做它以具有像DISPATCHER(int, myfunction, int, a, int, b) DISPATCHER(int, myfunction2, int, a, int, b, int, c) 这样的调用约定和像#define DISPATCHER(rettype, fnname, type1, var1, ...) 这样的启动宏
  • @JacekCz 因为它是一种不同的语言,即使每个人都同意它更好(我当然同意,但其他人不同意),迁移并不总是像看起来那样微不足道。

标签: c preprocessor variadic-macros


【解决方案1】:

一种可能性是将宏调用语法更改为更像DISPATCHER(int, myfunction2, int, a, int, b, int, c),其中变量名称和类型作为单独的宏参数传递。然后宏扩展可以在构建原型时使用类型信息,而在调用子函数时只使用参数名称。这要求您按名称访问所有单个参数,因此您必须提前知道参数的数量。许多具有不同参数计数的函数会使这种方法相当尴尬。

另一种选择是重构函数以使用varargs 而不是单个参数。然后宏扩展不需要处理参数列表,只需将其自己的va_list 传递给每个子函数(现在可以硬编码)。这避免了宏观问题,但您只是用它们来换取与varargs 相关的问题。

最近几次我需要做这样的事情,我刚刚编写了一个简短的脚本,可以预处理源文件并生成所需的代码。具有更好文本处理能力的语言(如 Python 或 Ruby)使这项工作变得更加容易。预处理器并不是真正为元编程而设计的,因此虽然可以让它为这样的东西工作,但使用不同的工具通常更快、更容易、更不容易出错。

【讨论】:

    【解决方案2】:

    这里不需要指定参数names;只要您将参数名称与相应的参数名称匹配,宏中任何合理唯一的名称都可以解决问题。只生成这些名称可能比传递它更容易。

    这让我们的任务是扩展如下:

    DISPATCHER(int, myfunction, int, int)
    DISPATCHER(int, myfunction2, int, int, int)
    DISPATCHER(int, myfunction3, int)
    

    对于语义相关的事情,我不喜欢直接跳转到...,所以我建议改为使用三参数宏,其中第三个参数本身是一个类型列表:

    DISPATCHER(int, myfunction, (int, int))
    DISPATCHER(int, myfunction2, (int, int, int))
    DISPATCHER(int, myfunction3, (int))
    

    使用 boost 预处理器,您可以这样做:

    #include <boost/preprocessor/repetition/enum.hpp>
    #include <boost/preprocessor/tuple/size.hpp>
    #include <boost/preprocessor/tuple/elem.hpp>
    #include <boost/preprocessor/cat.hpp>
    
    #define PARAMETER_M(z,OFFSET_,DATA_) \
       BOOST_PP_TUPLE_ELEM(OFFSET_,DATA_) BOOST_PP_CAT(arg_,OFFSET_)
    #define ARGUMENT_M(z,OFFSET_,DATA_) \
       BOOST_PP_CAT(arg_,OFFSET_)
    
    #define DISPATCH(RETVAL_,NAME_,ARGLIST_) \
       RETVAL_ NAME_ ( BOOST_PP_ENUM(BOOST_PP_TUPLE_SIZE(ARGLIST_), PARAMETER_M,ARGLIST_) ) \
       { \
             if (flag) return NAME_ ## flag1 \
             ( BOOST_PP_ENUM(BOOST_PP_TUPLE_SIZE(ARGLIST_), ARGUMENT_M, ARGLIST_) ); \
             else return NAME_ ## flag0 \
             ( BOOST_PP_ENUM(BOOST_PP_TUPLE_SIZE(ARGLIST_), ARGUMENT_M, ARGLIST_) ); \
       }
    

    此实现使用BOOST_PP_ENUM 生成参数和参数列表。在这两种情况下,ARG 都是我们的类型元组(例如,(int,int,short,char*))。

    PARAMETER_MMACRO,它通过从类型列表中选择OFFSET_-th 参数生成参数列表,然后将arg_ 粘贴到OFFSET_

    ARGUMENT_MMACRO,它通过简单地将arg_ 粘贴到OFFSET_ 来生成参数列表。

    【讨论】:

      猜你喜欢
      • 2016-07-15
      • 2011-07-02
      • 1970-01-01
      • 1970-01-01
      • 2014-04-13
      • 1970-01-01
      • 1970-01-01
      • 2011-01-08
      相关资源
      最近更新 更多