C++ 宏的可选参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3046889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:53:44  来源:igfitidea点击:

Optional Parameters with C++ Macros

c++macros

提问by Cenoc

Is there some way of getting optional parameters with C++ Macros? Some sort of overloading would be nice too.

是否有某种方法可以使用 C++ 宏获取可选参数?某种重载也会很好。

回答by Derek Ledbetter

Here's one way to do it. It uses the list of arguments twice, first to form the name of the helper macro, and then to pass the arguments to that helper macro. It uses a standard trick to count the number of arguments to a macro.

这是一种方法。它两次使用参数列表,首先形成辅助宏的名称,然后将参数传递给该辅助宏。它使用标准技巧来计算宏的参数数量。

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;
}

This makes it easier for the caller of the macro, but not the writer.

这使得宏的调用者更容易,而不是作者。

回答by David Sorkovsky

With great respect to Derek Ledbetter for his answer — and with apologies for reviving an old question.

非常尊重 Derek Ledbetter 的回答——并为重新提出一个老问题而道歉。

Getting an understanding of what it was doing and picking up elsewhere on the ability to preceed the __VA_ARGS__with ##allowed me to come up with a variation...

了解它在做什么并在其他地方了解在__VA_ARGS__with 之前的能力##让我想出了一个变化......

// 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__)\
                                         ) 

For non-experts like me who stumble upon the answer, but can't quite see how it works, I'll step through the actual processing, starting with the following code...

对于像我这样偶然发现答案但不太清楚它是如何工作的非专家,我将逐步完成实际处理,从以下代码开始......

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 

Becomes...

变成...

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: Remove the #define for XXX_0 to get a compile error [ie: if a no-argument option is not allowed].

PS:删除XXX_0的#define以获得编译错误[即:如果不允许无参数选项]。

PPS: Would be nice to have the invalid situations (eg: 5) be something that gives a clearer compilation error to the programmer!

PPS:如果无效情况(例如:5)能够为程序员提供更清晰的编译错误,那就太好了!

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

PPPS:我不是专家,所以我很高兴听到评论(好、坏或其他)!

回答by sepp2k

C++ macros haven't changed from C. Since C didn't have overloading and default arguments for functions, it certainly didn't have them for macros. So to answer your question: no, those features don't exist for macros. Your only option is to define multiple macros with different names (or not use macros at all).

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.

作为旁注:在 C++ 中,尽可能远离宏通常被认为是一种很好的做法。如果您需要这样的功能,那么您很有可能会过度使用宏。

回答by Jason Deng

With greatest respect to Derek Ledbetter, David Sorkovsky, Syphorlatefor their answers, together with the ingenious method to detect empty macro arguments by Jens Gustedtat

Derek LedbetterDavid SorkovskySyphorlate的回答,以及Jens Gustedt

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

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

finally I come out with something that incorporates all the tricks, so that the solution

最后我想出了一些包含所有技巧的东西,以便解决方案

  1. Uses only standard C99macros 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=c99to your compiler if you wish =)
  2. Works for zero argument, as well as unlimited number of arguments, if you expand it further to suit your needs
  3. Works reasonably cross-platform, at least tested for

    • GNU/Linux + GCC(GCC 4.9.2 on CentOS 7.0 x86_64)
    • GNU/Linux + CLANG/LLVM, (CLANG/LLVM 3.5.0 on CentOS 7.0 x86_64)
    • OS X + Xcode, (XCode 6.1.1 on OS X Yosemite 10.10.1)
    • Windows + Visual Studio, (Visual Studio 2013 Update 4 on Windows 7 SP1 64 bits)
  1. 仅使用标准的 C99宏来实现函数重载,不涉及 GCC/CLANG/MSVC 扩展(即, ##__VA_ARGS__GCC/CLANG特定表达式的逗号吞咽##__VA_ARGS__,MSVC 的隐式吞咽)。因此,--std=c99如果您愿意,请随时将缺少的内容传递给您的编译器 =)
  2. 适用于零参数以及无限数量的参数,如果您进一步扩展它以满足您的需要
  3. 合理地跨平台工作,至少经过测试

    • GNU/Linux + GCC(CentOS 7.0 x86_64 上的 GCC 4.9.2)
    • GNU/Linux + CLANG/LLVM,(CentOS 7.0 x86_64 上的 CLANG/LLVM 3.5.0)
    • OS X + Xcode,(OS X Yosemite 10.10.1 上的 XCode 6.1.1)
    • Windows + Visual Studio,(Windows 7 SP1 64 位上的 Visual Studio 2013 Update 4)

For the lazies, just skip to the very last of this post to copy the source. Below is the detailed explanation, which hopefully helps and inspires all people looking for the general __VA_ARGS__solutions like me. =)

对于懒惰的人,只需跳到本文的最后即可复制源代码。下面是详细的解释,希望能帮助和激励所有__VA_ARGS__像我一样寻找通用解决方案的人。=)

Here's how it goes. First define the user-visible overloaded "function", I named it create, and the related actual function definition realCreate, and the macro definitions with different number of arguments CREATE_2, CREATE_1, CREATE_0, as shown below:

这是怎么回事。首先定义用户可见的重载“功能”,我把它命名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)

The MACRO_CHOOSER(__VA_ARGS__)part ultimately resolves to the macro definition names, and the second (__VA_ARGS__)part comprises their parameter lists. So a user's call to create(10)resolves to CREATE_1(10), the CREATE_1part comes from MACRO_CHOOSER(__VA_ARGS__), and the (10)part comes from the second (__VA_ARGS__).

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

The MACRO_CHOOSERuses the trick that, if __VA_ARGS__is empty, the following expression is concatenated into a valid macro call by the preprocessor:

MACRO_CHOOSER使用了,如果招__VA_ARGS__是空的,下面的表达式连接成由预处理器有效的宏调用:

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

Ingeniusly, we can define this resulting macro call as

巧妙地,我们可以将这个结果宏调用定义为

#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__ ())

so the calls of

所以的呼唤

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

are actually expanded to

实际上扩展到

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);

As the macro name suggests, we are to count number of arguments later. Here comes another trick: the preprocessor only does simple text replacement. It infers the number of arguments of a macro call merely from the number of commas it sees inside the parentheses. The actual "arguments" separated by commas need not to be of valid syntax. They can be any text. That's to say, in the above example, NO_ARG_EXPANDER 10 ()is counted as 1 argument for the middle call. NO_ARG_EXPANDER 20and 20 ()are counted as 2 arguments for the bottom call respectively.

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

If we use the following helper macros to further expand them

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

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

The trailing ,after CREATE_1is a work-around for GCC/CLANG, suppressing a (false positive) error saying that ISO C99 requires rest arguments to be usedwhen passing -pedanticto your compiler. The FUNC_RECOMPOSERis a work-around for MSVC, or it can not count number of arguments (i.e., commas) inside the parentheses of macro calls correctly. The results are further resolved to

尾随,之后CREATE_1是GCC / CLANG一个变通办法,抑制(假阳性)错误说,ISO C99 requires rest arguments to be used路过的时候-pedantic给你的编译器。这FUNC_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);

As the eagle-eyed you may have seen, the last only step we need is to employ a standard argument counting trick to finally pick the wanted macro version names:

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

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

which resolves the results to

将结果解析为

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

and certainly gives us the desired, actual function calls:

并且肯定给了我们想要的、实际的函数调用:

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

Putting all together, with some rearrangement of statements for better readability, the whole source of the 2-argument exampleis here:

总而言之,为了更好的可读性,对语句进行了一些重新排列,2 参数示例整个源代码在这里:

#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;
}

Although complicated, ugly, burdening the API developer, there comes a solution for overloading and setting optional parameters of C/C++ functions to us crazy people. The usage of the out-coming overloaded APIs become very enjoyable and pleasant. =)

虽然复杂,丑陋,给API开发人员带来负担,但为我们这些疯狂的人提供了一个重载和设置C/C++函数可选参数的解决方案。输出重载 API 的使用变得非常愉快和愉快。=)

If there is any further possible simplification of this approach, please do let me know at

如果这种方法有任何进一步可能的简化,请告诉我

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

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! =)

再次特别感谢所有启发并带领我完成这部作品的杰出人士!=)

回答by Syphorlate

For anyone painfully searching some VA_NARGS solution that works with Visual C++. Following macro worked for me flawlessly(also with zero parameters!) in visual c++ express 2010:

对于任何痛苦地搜索一些适用于 Visual C++ 的 VA_NARGS 解决方案的人。以下宏在visual c ++ express 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

If you want a macro with optional parameters you can do:

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

//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__))) 

That worked for me aswell in vc. But it doesn't work for zero parameters.

这在 vc 中也对我有用。但它不适用于零参数。

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

回答by Paul R

gcc/g++supports varargs macrosbut I don't think this is standard, so use it at your own risk.

gcc/g++支持varargs 宏,但我认为这不是标准的,因此请自担风险使用它。

回答by Joe D

#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 */
}

DISCLAIMER: Mostlyharmless.

免责声明:大部分是无害的。

回答by Pontus Gagge

That's not really what the preprocessor is designed for.

这并不是预处理器的真正目的。

That said, if you want to enter into the area of seriously challenging macro programming with a modicum of readability, you should take a look at the Boost preprocessor library. After all, it wouldn't be C++ if there weren't three completely Turing compatible levels of programming (preprocessor, template metaprogramming, and base level C++)!

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

回答by Edward Strange

#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)

You know at the point of call how many args you're going to pass in so there's really no need for overloading.

您在调用时就知道要传入多少参数,因此实际上不需要重载。

回答by Megamozg

More concise version of Derek Ledbetter's code:

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;
}