[英]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.