如何在函数定义和函数调用中使用可变宏参数?

AJM*_*eld 3 c macros variadic-macros

我试图使用宏根据宏的参数定义几个类似的函数。然而,结果函数需要采用的参数的数量和类型在所有函数中并不相同,但我还需要将函数的所有参数传递到函数体内的另一个可变参数函数中。

\n

我想要完成的事情的一个最小例子:

\n
#define COMMAND(__COMMAND__, __FORMAT__, ...) \\\n  void __COMMAND__ ( __VA_ARGS__ ) {          \\\n    printf( __FORMAT__, ##__VA_ARGS__ );      \\\n  }\n\nCOMMAND( Start,        "m start %c\\r", (char) unit )\nCOMMAND( Home,         "m home\\r" )\nCOMMAND( Add_To_Chart, "cv 0 %d %d\\r", (int) ch1, (int) ch2 )\n// literally hundreds of additional COMMANDs needed here.\n
Run Code Online (Sandbox Code Playgroud)\n

(请注意,该函数的实际逻辑要复杂得多。)

\n

但是,我无法找出既作为函数定义中的参数列表又作为函数调用中的参数列表有效的语法。

\n

使用表单(type)arg对于函数定义来说不是有效的语法,但我可以将它传递给printf就好(它被视为强制转换)。

\n
COMMAND( A, "cv 0 %d %d\\r", (int)ch1, (int)ch2 )\n// error: expected declaration specifiers or \xe2\x80\x98...\xe2\x80\x99 before \xe2\x80\x98(\xe2\x80\x99 token\n// void A ( (int)ch1, (int)ch2 ) {\n//   printf( "cv 0 %d %d\\r", (int)ch1, (int)ch2 );\n// }\n
Run Code Online (Sandbox Code Playgroud)\n

以另一种方式执行此操作,type(arg), 似乎适用于函数声明,但函数样式强制转换仅在 C++ 中可用,而在 C 中不可用,因此它在 上失败printf

\n
COMMAND( B, "cv 0 %d %d\\r", int(ch1), int(ch2) )\n// error: expected expression before \xe2\x80\x98int\xe2\x80\x99\n// void B ( int(ch1), int(ch2) ) {\n//   printf( "cv 0 %d %d\\r", int(ch1), int(ch2) );\n// }\n
Run Code Online (Sandbox Code Playgroud)\n

如何使用可变参数宏参数作为函数的参数定义和传递给另一个函数的参数?

\n

Kam*_*Cuk 6

不要__*在代码中创建标识符。__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)
Run Code Online (Sandbox Code Playgroud)

扩展到:

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); }
Run Code Online (Sandbox Code Playgroud)

您可以通过在参数数量上重载单个“对每对参数应用一个宏并将它们与此连接,如果为空则使用此”宏来使代码更加通用。

#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)
Run Code Online (Sandbox Code Playgroud)