簡體   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