使用 C + + 宏的可选参数

有没有什么方法可以用 C + + 宏获得可选的参数。

122373 次浏览

gcc/g++支持 Varargs 宏,但我不认为这是标准的,所以使用它的风险自负。

C + + 宏没有从 C 改变。因为 C 没有函数的重载参数和默认参数,所以当然也没有宏的参数。所以回答你的问题: 不,这些特性不存在于宏中。您唯一的选择是使用不同的名称定义多个宏(或者根本不使用宏)。

As a sidenote: In C++ it's generally considered good practice to move away from macros as much as possible. If you need features like this, there's a good chance you're overusing macros.

根据您的需要,您可以使用 Var args和宏来实现这一点。现在,可选参数或宏重载,没有这样的东西。

这不是预处理器的设计初衷。

也就是说,如果您想要进入具有一定可读性的严重挑战宏编程的领域,那么您应该看看 提升预处理程序库。毕竟,如果没有三个完全兼容的图灵级别的编程(预处理器、模板超编程和基本级别的 c + +) ,就不会是 C + + !

#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

你知道在调用的时候要传入多少个 args 所以真的不需要超载。

只有一个办法。它使用参数列表两次,首先形成助手宏的名称,然后将参数传递给该助手宏。它使用一个标准技巧来计算宏的参数数量。

enum
{
plain = 0,
bold = 1,
italic = 2
};


void PrintString(const char* message, int size, int style)
{
}


#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)


#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )


#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)


int main(int argc, char * const argv[])
{
PRINT_STRING("Hello, World!");
PRINT_STRING("Hello, World!", 18);
PRINT_STRING("Hello, World!", 18, bold);


return 0;
}

这使得宏的调用者(而不是编写者)更加容易。

#include <stdio.h>


#define PP_NARG(...) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define PP_RSEQ_N() \
63,62,61,60,                   \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0


#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b


#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
" actually means will be build in %s\n", (answer), (computer), (location))


int
main (int argc, char *argv[])
{
THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

免责声明: 差不多吧无害。

我非常尊重德里克•莱德贝特(Derek Ledbetter)的回答ーー并为重提一个老问题道歉。

理解了它在做什么,并在其他地方学习了 __VA_ARGS__##之前的能力,这让我想出了一个变化..。

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments>
#define XXX_1(A)                    <code for one argument>
#define XXX_2(A,B)                  <code for two arguments>
#define XXX_3(A,B,C)                <code for three arguments>
#define XXX_4(A,B,C,D)              <code for four arguments>


// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC


// The macro that the programmer uses
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
XXX_4(__VA_ARGS__),\
XXX_3(__VA_ARGS__),\
XXX_2(__VA_ARGS__),\
XXX_1(__VA_ARGS__),\
XXX_0(__VA_ARGS__)\
)

对于像我这样的非专业人士,偶然发现了答案,但不能完全看到它是如何工作的,我将逐步通过实际的处理,从以下代码开始..。

XXX();
XXX(1);
XXX(1,2);
XXX(1,2,3);
XXX(1,2,3,4);
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process

变成..。

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

Which becomes just the sixth argument...

XXX_0();
XXX_1(1);
XXX_2(1,2);
XXX_3(1,2,3);
XXX_4(1,2,3,4);
5;

PS: 删除 # Definition for XXX _ 0以获得编译错误[即: 如果不允许使用无参数选项]。

PPS: 如果无效的情况(例如: 5)能给程序员一个更清晰的编译错误就好了!

PPPS: I'm not an expert, so I'm very happy to hear comments (good, bad or other)!

上面的例子(来自 Derek Ledbetter、 David Sorkovsky 和 Joe D)没有一个用宏来计算参数,我使用的是 Microsoft VCC 10。__VA_ARGS__参数总是被认为是一个单独的参数(使用 ##或不使用 ##进行标记化) ,因此这些示例所依赖的参数转换不起作用。

所以,简短的回答,正如上面许多其他人所说的: 不,你不能重载宏或对它们使用可选参数。

对于那些苦苦搜索使用 Visual C + + 的 VA _ NARGS 解决方案的人来说。下面的宏对我来说非常有用(也没有任何参数!)2010年:

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))) : 0

如果你想要一个带有可选参数的宏,你可以这样做:

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__)))

我在风险投资公司也是这样,但是在零参数的情况下就不行了。

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8

更简洁版本的 Derek Ledbetter 的代码:

enum
{
plain = 0,
bold = 1,
italic = 2
};




void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}




#define PRINT_STRING(...) PrintString(__VA_ARGS__)




int main(int argc, char * const argv[])
{
PRINT_STRING("Hello, World!");
PRINT_STRING("Hello, World!", 18);
PRINT_STRING("Hello, World!", 18, bold);


return 0;
}

最大限度地尊重 Derek Ledbetter大卫 · 索尔科夫斯基精油为他们的答案,再加上巧妙的方法来检测空的宏参数由 Jens Gustedt

Https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

最后我想出了一个办法,把所有的技巧结合起来,这样解决方案

  1. Uses only 标准 C99 macros to achieve function overloading, no GCC/CLANG/MSVC extension involved (i.e., comma swallowing by the specific expression , ##__VA_ARGS__ for GCC/CLANG, and implicit swallowing by ##__VA_ARGS__ for MSVC). So feel free to pass the missing --std=c99 to your compiler if you wish =)
  2. 适用于 零参数,以及 无限数量的参数,如果您进一步扩展它以满足您的需要
  3. 工作合理 跨平台,至少测试

    • GNU/Linux + GCC (CentOS 7.0 x86 _ 64上的 GCC 4.9.2)
    • GNU/Linux + CLANG/LLVM ,(CLANG/LLVM 3.5.0 on CentOS 7.0 x86 _ 64)
    • OS X + XCode ,(OS X Yosemite 10.10.1上的 XCode 6.1.1)
    • Windows + Visual Studio (Visual Studio 2013 Update 4 on Windows 7 SP164 bit)

对于懒人,直接跳到这篇文章的最后来复制源代码。下面是详细的解释,希望能帮助和启发所有人寻找一般 __VA_ARGS__解决方案,像我一样。=)

事情是这样的。首先定义用户可见的重载“函数”,我把它命名为 create,以及相关的实际函数定义 realCreate,以及具有不同数量参数的宏定义 CREATE_2CREATE_1CREATE_0,如下所示:

#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)


void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}


#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

MACRO_CHOOSER(__VA_ARGS__)部分最终解析为宏定义名称,而第二个 (__VA_ARGS__)部分包含它们的参数列表。因此,用户对 create(10)的调用解析为 CREATE_1(10)CREATE_1部分来自 MACRO_CHOOSER(__VA_ARGS__),而 (10)部分来自第二个 (__VA_ARGS__)

MACRO_CHOOSER使用这样的技巧: 如果 __VA_ARGS__为空,下面的表达式将被预处理器连接到一个有效的宏调用中:

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

巧妙地,我们可以将这个生成的宏调用定义为

#define NO_ARG_EXPANDER() ,,CREATE_0

Note the two commas, they are explained soon. The next useful macro is

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

所以

create();
create(10);
create(20, 20);

实际上扩展到

CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);

正如宏名所暗示的那样,我们将在以后计算参数的数量。这里有另一个技巧: 预处理器只做简单的文本替换。它仅仅从它在括号内看到的逗号数量推断出宏调用的参数数量。用逗号分隔的实际“参数”不必具有有效的语法。它们可以是任何文本。也就是说,在上面的例子中,NO_ARG_EXPANDER 10 ()被算作中间调用的1个参数。NO_ARG_EXPANDER 2020 ()分别作为底部调用的2个参数。

如果我们使用以下助手宏来进一步展开它们

##define CHOOSE_FROM_ARG_COUNT(...) \
FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
FUNC_CHOOSER argsWithParentheses

CREATE_1后面的 ,是 GCC/CLANG 的一种解决方案,它在将 -pedantic传递给编译器时抑制了一个(假阳性)错误,即 ISO C99 requires rest arguments to be usedFUNC_RECOMPOSER是 MSVC 的解决方案,或者它不能正确地计算宏调用括号内的参数数量(例如,逗号)。结果进一步决定

FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);

正如您可能已经看到的那样,我们需要做的最后一步是使用一个标准的参数计数技巧来最终选择所需的宏版本名称:

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

它将结果解析为

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

当然也给了我们想要的,实际的函数调用:

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

为了提高可读性,对语句进行了一些重新排列,将所有这些放在一起,两个参数例子的全部来源就在这里:

#include <stdio.h>


void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}


#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)


#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)


int main()
{
create();
create(10);
create(20, 20);
//create(30, 30, 30);  // Compilation error
return 0;
}

尽管复杂、丑陋、给 API 开发人员带来负担,但对于我们这些疯狂的人来说,还是有一个解决方案可以重载和设置 C/C + + 函数的可选参数。即将推出的重载 API 的使用变得非常令人愉快。=)

如果有任何进一步可能的简化此方法,请让我知道在

https://github.com/jason-deng/C99FunctionOverload

Again special thanks to all of the brilliant people that inspired and led me to achieve this piece of work! =)

您可以从 boost库中使用 BOOST_PP_OVERLOAD

来自 official boost doc的例子:

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>


#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)


#if !BOOST_PP_VARIADICS_MSVC


#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)


#else


// or for Visual C++


#define MACRO_ADD_NUMBERS(...) \
BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())


#endif


MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9

作为一个可怕的宏怪物的超级粉丝,我想扩展 Jason Deng 的答案,并使其实际可用。(无论好坏)原来的不是很好使用,因为你需要修改大字母汤每次你想要一个新的宏,它甚至更糟糕,如果你需要不同数量的参数。

所以我用这些特性做了一个版本:

  • 0参数情况下工作
  • 1到16个论点,没有任何修改,混乱的部分
  • 易于编写更多宏函数
  • 在 gcc10,clang 9,Visual Studio 2017中测试

目前我只做了16个论点最多,但如果你需要更多(真的现在吗?你只是越来越傻了...)你可以编辑 FUNC _ CHOOSER 和 CHOOSE _ FROM _ ARG _ COUNT,然后添加一些逗号到 NO _ ARG _ EXPANDER。

关于实现的更多细节,请参见 Jason Deng 的精彩回答,但我只是把代码放在这里:

#include <stdio.h>


void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}


// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)


// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
do { \
/* put whatever code you want in the last macro */ \
realCreate(x, y); \
} while(0)




int main()
{
create();
create(10);
create(20, 20);
//create(30, 30, 30);  // Compilation error
return 0;
}

不是直接回答问题,而是使用与 大卫 · 索尔科夫斯基答案相同的技巧,并给出一个如何构建复杂宏的清晰示例。

只要用 g++ -E test.cpp -o test && cat test编译这个:

// #define GET_FIRST_ARG_0_ARGS(default) (default)
// #define GET_FIRST_ARG_1_ARGS(default, a) (a)
// #define GET_FIRST_ARG_2_ARGS(default, a, b) (a)
// #define GET_FIRST_ARG_3_ARGS(default, a, b, c) (a)
// #define GET_FIRST_ARG_4_ARGS(default, a, b, c, d) (a)
#define GET_FIRST_ARG_MACROS(default, a, b, c, d, macro, ...) macro


#define GET_FIRST_ARG(default, ...) GET_FIRST_ARG_MACROS( \
,##__VA_ARGS__, \
GET_FIRST_ARG_4_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_3_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_2_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_1_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_0_ARGS(default, ##__VA_ARGS__), \
)


"0,"; GET_FIRST_ARG(0);
"0,1"; GET_FIRST_ARG(0,1);
"0,1,2"; GET_FIRST_ARG(0,1,2);
"0,1,2,3"; GET_FIRST_ARG(0,1,2,3);
"0,1,2,3,4"; GET_FIRST_ARG(0,1,2,3,4);

查看输出:

# 1 "test.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/x86_64-linux-gnu/include/stdc-predef.h" 1 3
# 1 "<command-line>" 2
# 1 "test.cpp"
# 16 "test.cpp"
"0,"; GET_FIRST_ARG_0_ARGS(0);
"0,1"; GET_FIRST_ARG_1_ARGS(0, 1);
"0,1,2"; GET_FIRST_ARG_2_ARGS(0, 1,2);
"0,1,2,3"; GET_FIRST_ARG_3_ARGS(0, 1,2,3);
"0,1,2,3,4"; GET_FIRST_ARG_4_ARGS(0, 1,2,3,4);

现在,一个完整的工作程序是:

#include <iostream>


#define GET_FIRST_ARG_0_ARGS(default) (default)
#define GET_FIRST_ARG_1_ARGS(default, a) (a)
#define GET_FIRST_ARG_2_ARGS(default, a, b) (a)
#define GET_FIRST_ARG_3_ARGS(default, a, b, c) (a)
#define GET_FIRST_ARG_4_ARGS(default, a, b, c, d) (a)
#define GET_FIRST_ARG_MACROS(default, a, b, c, d, macro, ...) macro


#define GET_FIRST_ARG(default, ...) GET_FIRST_ARG_MACROS( \
,##__VA_ARGS__, \
GET_FIRST_ARG_4_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_3_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_2_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_1_ARGS(default, __VA_ARGS__), \
GET_FIRST_ARG_0_ARGS(default, ##__VA_ARGS__), \
)


int main(int argc, char const *argv[]) {
"0,"; GET_FIRST_ARG(0);
"0,1"; GET_FIRST_ARG(0,1);
"0,1,2"; GET_FIRST_ARG(0,1,2);
"0,1,2,3"; GET_FIRST_ARG(0,1,2,3);
"0,1,2,3,4"; GET_FIRST_ARG(0,1,2,3,4);


std::cerr << "0, == " << GET_FIRST_ARG(0) << std::endl;
std::cerr << "0,1 == " << GET_FIRST_ARG(0,1) << std::endl;
std::cerr << "0,1,2 == " << GET_FIRST_ARG(0,1,2) << std::endl;
std::cerr << "0,1,2,3 == " << GET_FIRST_ARG(0,1,2,3) << std::endl;
std::cerr << "0,1,2,3,4 == " << GET_FIRST_ARG(0,1,2,3,4) << std::endl;
return 0;
}

通过使用 g++ test.cpp -o test && ./test编译,它将输出以下内容:

0, == 0
0,1 == 1
0,1,2 == 1
0,1,2,3 == 1
0,1,2,3,4 == 1

注意: 在宏内容周围使用 ()作为 #define GET_FIRST_ARG_1_ARGS(default, a) (a)非常重要,当 a不是整数时,不要中断不明确的表达式。

第二个论点的额外宏观:

#define GET_SECOND_ARG_0_ARGS(default) (default)
#define GET_SECOND_ARG_1_ARGS(default, a) (default)
#define GET_SECOND_ARG_2_ARGS(default, a, b) (b)
#define GET_SECOND_ARG_3_ARGS(default, a, b, c) (b)
#define GET_SECOND_ARG_4_ARGS(default, a, b, c, d) (b)
#define GET_SECOND_ARG_MACROS(default, a, b, c, d, macro, ...) macro


#define GET_SECOND_ARG(default, ...) GET_SECOND_ARG_MACROS( \
,##__VA_ARGS__, \
GET_SECOND_ARG_4_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_3_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_2_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_1_ARGS(default, __VA_ARGS__), \
GET_SECOND_ARG_0_ARGS(default, ##__VA_ARGS__), \
)