使用C ++macros的可选参数

有什么方法获得可选参数与C ++macros? 某种超载也会很好。

C ++macros没有从C中改变。因为C没有重载和函数的默认参数,所以它当然没有macros。 所以要回答你的问题:不,那些macros的function不存在。 你唯一的select是定义多个不同名称的macros(或者根本不使用macros)。

作为一个旁注:在C ++中,通常认为尽可能远离macros是一种很好的做法。 如果你需要这样的function,你很可能会过度使用macros。

这是一个办法。 它使用参数列表两次,首先形成助手macros的名称,然后将parameter passing给该助手macros。 它使用一个标准的技巧来计算一个macros的参数数量。

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

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

对德里克·莱德贝特的回答非常尊重 – 并且为了恢复一个老问题而道歉。

了解它在做什么,并在__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) ); 

这只是第六个参数…

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

PS:删除XXX_0的#define以得到编译错误[ie:如果没有参数选项是不允许的]。

PPS:有一些无效的情况(例如:5)会给程序员带来更清晰的编译错误!

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

德里克·莱德贝特Derek Ledbetter)大卫·索尔 科夫斯基David Sorkovsky) 比特Syphorlate)的回答,以及Jens Gustedt

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

最后我拿出一些结合了所有技巧的东西,这样的解决scheme

  1. 只使用标准的C99macros来实现函数重载,不涉及GCC / CLANG / MSVC扩展(即通过特定expression式的逗号吞,GCC / CLANG的##__VA_ARGS__以及用MSVC的##__VA_ARGS__隐式吞咽)。 所以,如果你愿意的话,随意把缺less的--std=c99传递给你的编译器=)
  2. 适用于零参数 ,以及无限数量的参数 ,如果您进一步扩展以满足您的需求
  3. 工作合理跨平台 ,至lesstesting

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

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

这是怎么回事。 首先定义用户可见的重载“函数”,我将其命名为create ,并将相关的实际函数定义为realCreate ,以及具有不同数量参数CREATE_2CREATE_1CREATE_0的macros定义,如下所示:

 #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__)部分最终parsing为macros定义名称,第二个(__VA_ARGS__)部分包含它们的参数列表。 因此,用户create(10)的调用将parsing为CREATE_1(10)CREATE_1部分来自MACRO_CHOOSER(__VA_ARGS__)(10)部分来自第二个(__VA_ARGS__)

MACRO_CHOOSER使用的技巧是,如果__VA_ARGS__为空,则预处理器将下列expression式连接成有效的macros调用:

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

独创,我们可以定义这个结果macros调用为

 #define NO_ARG_EXPANDER() ,,CREATE_0 

注意这两个逗号,他们很快解释。 下一个有用的macros是

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

正如macros观名字所暗示的那样,我们将在稍后计算一些参数。 还有另一个窍门:预处理器只做简单的文本replace。 它仅从它在括号内看到的逗号的数目来推断macros调用的参数的数目。 用逗号分隔的实际“参数”不必是有效的语法。 他们可以是任何文本。 也就是说,在上面的例子中, NO_ARG_EXPANDER 10 ()被计为中间调用的1个参数。 NO_ARG_EXPANDER 2020 ()分别计为底部调用的2个参数。

如果我们使用下面的助手macros来进一步扩展它们

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

CREATE_1之后的CREATE_1是GCC / CLANG的解决方法,它抑制了一个(错误的肯定)错误,说ISO C99 requires rest arguments to be used在传递给编译器时ISO C99 requires rest arguments to be usedFUNC_RECOMPOSER是MSVC的变通方法,或者它无法正确计算macros调用括号内的参数(即逗号)的数目。 结果进一步解决了

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

正如你可能已经看到的鹰眼,我们需要的最后一个步骤是使用一个标准的参数计数技巧来最终select想要的macros版本名称:

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

解决的结果

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

并肯定给我们所需的,实际的function调用:

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

综合起来,为了更好的可读性对语句进行一些重新排列,这个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; } 

虽然复杂,丑陋,加重了API开发者的负担,但是还是有一个解决scheme,可以为C / C ++函数重载和设置可选参数给我们疯狂的人。 即将到来的重载API的使用变得非常愉快和愉快。 =)

如果还有其他可能的简化方法,请让我知道

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

再次特别感谢所有那些鼓舞并带领我实现这一切的杰出人士! =)

任何人都会痛苦地search一些与Visual C ++一起工作的VA_NARGS解决scheme。 下面的macros在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 

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

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

gcc / g++支持可变参数macros,但我不认为这是标准的,所以使用它需要您自担风险。

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

免责声明:基本无害。

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

也就是说,如果你想进入一个严格挑战性的macros编程领域,并且有一点可读性,那么你应该看一下Boost预处理器库 。 毕竟,如果没有三个完全图灵兼容的编程水平(预处理器,模板元编程和基本级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) 

你在通话的时候知道你要通过多less个参数,所以实际上不需要超载。

取决于你需要什么,你可以用var args和macros来完成。 现在,可选参数或macros重载,没有这样的事情。

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,David Sorkovsky和Joe D)使用微软VCC 10为macros计数参数__VA_ARGS__参数总是被认为是一个单独的参数(标记 – 用##表示它),所以这些例子所依赖的论点转移是行不通的。

所以,简单的回答,正如上面的许多人所说:不,你不能重载macros或使用可选的参数。