繁体   English   中英

可变参数宏中的C99兼容嵌套调用

[英]C99 compatible nested calls within variadic macros

需要一种方法来支持带有可选参数的可变参数宏中的嵌套调用。 相同,但C99兼容

##运算符的GNU gcc扩展可防止嵌套调用被扩展,请参阅下面的代码。

#define send(obj, msg, ...) find_method(obj, msg)(obj, ##__VA_ARGS__)

/* Ok */
send(0, "+", 1);
find_method(0, "+")(0, 1);

/* Ok. nested call as macros named argument */
send(send(5, "increment"), "+", 1);
find_method(find_method(5, "increment")(5), "+")(find_method(5, "increment")(5), 1);

/* Fail. nested call as macros variable argument i.e. `send` macro is not expanded */
send(0, "+", send(5, "increment"));
find_method(0, "+")(0, send(5, "increment"));

/*
 * preprocess with next commands
 *
 * gcc-4.9   -Wall -std=c99 -E -c file.c | less
 * clang-3.8 -Wall -std=c99 -E -c file.c | less
 */

我修改了Jens Gustedt 解决方案以放置可选的逗号,请参阅下面的代码。

有更简洁的替代品吗?

#define _ARG16(              \
    _0,  _1,  _2,  _3,  _4,  \
    _5,  _6,  _7,  _8,  _9,  \
    _10, _11, _12, _13, _14, \
    _15, ...) _15
#define HAS_COMMA(...) \
    _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define _TRIGGER_PARENTHESIS_(...) ,

#define OPTIONAL_COMMA(...)                                             \
    _ISEMPTY(                                                           \
          /* test if there is just one argument, eventually an empty    \
             one */                                                     \
          HAS_COMMA(__VA_ARGS__),                                       \
          /* test if _TRIGGER_PARENTHESIS_ together with the argument   \
             adds a comma */                                            \
          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__),                 \
          /* test if the argument together with a parenthesis           \
             adds a comma */                                            \
          HAS_COMMA(__VA_ARGS__ (/*empty*/)),                           \
          /* test if placing it between _TRIGGER_PARENTHESIS_ and the   \
             parenthesis adds a comma */                                \
          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))      \
    )

#define SPACE_SYMBOL
#define COMMA_SYMBOL ,
#define ARG3(_0, _1, _2, ...) _2
#define OPTIONAL_COMMA_FROM(...) ARG3(__VA_ARGS__, SPACE_SYMBOL, COMMA_SYMBOL)

#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _ISEMPTY(_0, _1, _2, _3) \
    OPTIONAL_COMMA_FROM(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,

#define send(obj, msg, ...) \
    find_method(obj, msg)(obj OPTIONAL_COMMA(__VA_ARGS__) __VA_ARGS__)

/* Ok */
send(0, "+", 1);

/* Ok */
send(send(5, "increment"), "+", 1);

/* Ok */
send(0, "+", send(5, "increment"));

更新:

H沃尔特斯感谢你的想法。

首先没有注意到宏重载的方法

#define SEND_NO_ARG(obj, msg) find_method(obj, msg)(obj)
#define SEND_ARG(obj, msg, ...) find_method(obj, msg)(obj, __VA_ARGS__)

#define GET_18TH_ARG(arg1, arg2, arg3, arg4, arg5,        \
    arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,   \
    arg14, arg15, arg16, arg17, arg18, ...) arg18
#define SEND_MACRO_SELECTOR(...)                          \
    GET_18TH_ARG(__VA_ARGS__,                             \
        SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, \
        SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, \
        SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, \
        SEND_NO_ARG, )

#define send(...) SEND_MACRO_SELECTOR(__VA_ARGS__)(__VA_ARGS__)

这种方法稍微简洁一些(10个宏对12个),至少在我看来有点组织(更多的宏是通用的):

#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define FIRSTOFMANY(X,...) X
// This can be used as a pattern matcher on the first argument.
// Generally, the first argument is ignored; but if it's
// an object-like macro whose expansion has a comma, it can
// shift a new second argument in that's returned.
// Thus, you can build a "query pattern" as the first argument,
// pass a "default" as the second, and override the default with
// "matched pattern macros".
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(_,X,...) X
// Expands to a count of arguments (min 1, max 15).
#define COUNT(...) COUNT_I(__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,)
#define COUNT_I(_,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,X,...) X

#define send(obj, ...) \
    find_method(obj, FIRSTOFMANY(__VA_ARGS__,)) \
    ( \
      /* select a macro based on ... count (default SEND_LONG) */ \
      SECOND(GLUE(WHEN_SEND_VCNT_EQ_,COUNT(__VA_ARGS__)),SEND_LONG)\
      (obj, __VA_ARGS__) \
    )
#define SEND_LONG(X,Y,...) X,__VA_ARGS__
// Use FIRSTOFMANY when send's varying arg count is 1
#define WHEN_SEND_VCNT_EQ_1 , FIRSTOFMANY

...它也符合C99(不依赖于gnu扩展)。

到目前为止,宏重载技术提供了最短的代码。

#define SEND_NO_ARG(obj, msg) find_method(obj, msg)(obj)
#define SEND_ARG(obj, msg, ...) find_method(obj, msg)(obj, __VA_ARGS__)

#define GET_18TH_ARG(arg1, arg2, arg3, arg4, arg5,        \
    arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,   \
    arg14, arg15, arg16, arg17, arg18, ...) arg18
#define SEND_MACRO_SELECTOR(...)                          \
    GET_18TH_ARG(__VA_ARGS__,                             \
        SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, \
        SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, \
        SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, SEND_ARG, \
        SEND_NO_ARG, )

#define send(...) SEND_MACRO_SELECTOR(__VA_ARGS__)(__VA_ARGS__)
##运算符的GNU gcc扩展可防止嵌套调用被扩展,请参阅下面的代码。

如果您愿意使用gnu扩展,这里有一个更简洁的方法:

#define COMMAVA(...) ,##__VA_ARGS__
#define send(obj, msg, ...) find_method(obj, msg)(obj COMMAVA(__VA_ARGS__))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM