Как использовать переменные макропараметры как в определении функции, так и в вызове функции?
Я пытаюсь использовать макрос для определения нескольких похожих функций на основе параметров макроса. Однако число и типы параметров, которые должна принимать результирующая функция, не одинаковы для всех функций, но мне также нужно передать все аргументы функции в другую переменную функцию внутри тела функции.
Минимальный пример того, что я пытаюсь сделать:
#define COMMAND(__COMMAND__, __FORMAT__, ...) \
void __COMMAND__ ( __VA_ARGS__ ) { \
printf( __FORMAT__, ##__VA_ARGS__ ); \
}
COMMAND( Start, "m start %c\r", (char) unit )
COMMAND( Home, "m home\r" )
COMMAND( Add_To_Chart, "cv 0 %d %d\r", (int) ch1, (int) ch2 )
// literally hundreds of additional COMMANDs needed here.
(Обратите внимание, что реальная логика функции намного сложнее.)
Однако я не могу понять синтаксис, который действителен как список аргументов в определении функции, так и при вызове функции.
Используя форму (type)arg
неверный синтаксис для определения функции, но я могу передать его printf
просто отлично (это рассматривается как актерский состав).
COMMAND( A, "cv 0 %d %d\r", (int)ch1, (int)ch2 )
// error: expected declaration specifiers or ‘...’ before ‘(’ token
// void A ( (int)ch1, (int)ch2 ) {
// printf( "cv 0 %d %d\r", (int)ch1, (int)ch2 );
// }
Делая это по-другому, type(arg), appears to work for the function declaration, but function-style casts are only available in C++, not C, so it fails on
printf`.
COMMAND( B, "cv 0 %d %d\r", int(ch1), int(ch2) )
// error: expected expression before ‘int’
// void B ( int(ch1), int(ch2) ) {
// printf( "cv 0 %d %d\r", int(ch1), int(ch2) );
// }
Как я могу использовать макропеременные с переменным аргументом как в качестве определения параметров функции, так и в качестве параметров, передаваемых другой функции?
1 ответ
Не делай__*
идентификаторы в вашем коде.__COMMAND__
и__FORMAT__
делают ваш код недействительным.
Вы должны сообщить препроцессору о типах. Передайте их как отдельные токены, затем перемешайте, например, в отдельных цепочках, перегруженных количеством аргументов.
// Create function arguments
#define ARGS_0() void
#define ARGS_2(t1,v1) t1 v1
#define ARGS_4(t1,v1,t2,v2) t1 v1, t2 v2
#define ARGS_N(_4,_3,_2,_1,_0,N,...) ARGS##N
#define ARGS(...) ARGS_N(_0,##__VA_ARGS__,_4,_3,_2,_1,_0)(__VA_ARGS__)
// Pass arguments to printf with a leading comma.
#define PASS_0()
#define PASS_2(t1,v1) , v1
#define PASS_4(t1,v1,t2,v2) , v1, v2
#define PASS_N(_4,_3,_2,_1,_0,N,...) PASS##N
#define PASS(...) PASS_N(_0,##__VA_ARGS__,_4,_3,_2,_1,_0)(__VA_ARGS__)
#define COMMAND(cmd, fmt, ...) \
void cmd(ARGS(__VA_ARGS__)) { \
printf(fmt PASS(__VA_ARGS__)); \
}
COMMAND( Start, "m start %c\r", char, unit)
COMMAND( Home, "m home\r")
COMMAND( Add_To_Chart, "cv 0 %d %d\r", int, ch1, int, ch2)
расширяется до:
void Start(char unit) { printf("m start %c\r" , unit); }
void Home(void) { printf("m home\r" ); }
void Add_To_Chart(int ch1, int ch2) { printf("cv 0 %d %d\r" , ch1, ch2); }
Вы можете сделать код более общим, используя один макрос «применить макрос к каждой паре аргументов и соединить их с этим, а если он пуст, используйте этот» макрос, перегруженный по количеству аргументов.
#define ESC(...) __VA_ARGS__
#define APPLYTWOJOIN_0(f,j,e) ESC e
#define APPLYTWOJOIN_2(f,j,e,t,v) f(t,v)
#define APPLYTWOJOIN_4(f,j,e,t,v,...) f(t,v) ESC j \
APPLYTWOJOIN_2(f,j,e,__VA_ARGS__)
#define APPLYTWOJOIN_6(f,j,e,t,v,...) f(t,v) ESC j \
APPLYTWOJOIN_4(f,j,e,__VA_ARGS__)
#define APPLYTWOJOIN_8(f,j,e,t,v,...) f(t,v) ESC j \
APPLYTWOJOIN_6(f,j,e,__VA_ARGS__)
// etc.
#define APPLYTWOJOIN_N(_8,_7,_6,_5,_4,_3,_2,_1,_0,N,...) \
APPLYTWOJOIN##N
// For every two arguments in the list, apply function `f(a,b)` on it.
// Join every result of that function with `ESC j`.
// Expand empty result to `ESC e`.
#define APPLYTWOJOIN(f, j, e, ...) \
APPLYTWOJOIN_N(_0,##__VA_ARGS__,_8,_7,_6,_5,_4,_3,_2,_1,_0)\
(f,j,e,##__VA_ARGS__)
// Pass argument to printf. The leading comma is after format string.
#define PASS(t,v) , v
// Pass arguments in function parameter list.
#define ARGS(t,v) t v
#define COMMAND(cmd, fmt, ...) \
void cmd( APPLYTWOJOIN(ARGS, (,), (void), ##__VA_ARGS__) ) { \
printf(fmt APPLYTWOJOIN(PASS, (), (), ##__VA_ARGS__) ); \
}
COMMAND( Start, "m start %c\r", char, unit)
COMMAND( Home, "m home\r")
COMMAND( Add_To_Chart, "cv 0 %d %d\r", int, ch1, int, ch2)