Optional Parameters with C++ Macros
Asked Answered
M

16

140

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

Manilla answered 15/6, 2010 at 16:2 Comment(3)
Same for C: #11762203 Should be the same since the preprocessors are basically the same: #5086033Rosado
Maybe function overloads, default parameters, variadic templates or possibly the named parameter idiom are what you are looking forShealy
Please update your selected answer to the highly-upvoted ones with actual solutions, not the lowly-upvoted one saying No you can'tInterrogator
B
201

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.

Bantam answered 15/6, 2010 at 19:28 Comment(6)
This is pretty cool, but I dont think it would work if I just did PRINT_STRING. In that case there wouldnt be a default print out (and that's actually the case I want to utilize). Still +1 for really cool.Manilla
works for me in gcc (and it's very clever!) :-) but doesn't work for me in Visual Studio :-(Boleslaw
@TimGradwell - it's due to a bug in the MSVC compiler which they have acknowledged but haven't fixed in nearly a decade. However, workarounds are available.Janinejanis
Clever, but doesn't work for optional variadic macro arguments because of the 'push out' thing you have going on in `GET_4th_ARG'.Moxley
is that PRINT_STRING_MACRO_CHOOSER even needed? Can i replace with its inner body directly and call this whole thing with (__VA_ARGS__)?Ainslee
This is genius. Took me a while to understandMarxist
S
112

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

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

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

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

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

Salacious answered 11/1, 2012 at 4:7 Comment(8)
You could get a clear compilation error if you converted the selected argument which is supposed to be a MACRO name to string using # (the pound sign) and compared it's first n characters with the expected prefix and if there is no match, printed an informative error.Crean
why is the first argument always empty? why cant we just omit it: XXX_X(,##__VA_ARGS__,` ... XXX_X(, XXX_4(), XXX_3(), XXX_2(), XXX_1(), XXX_0() );`Angeliqueangelis
The empty first argument (comma) is important. ##__VA_ARGS__ if it’s preceded by a comma–it removes the comma if ##__VA_ARGS__ expands to nothing. You can see it in the "Becomes..." example as the first (no arguments) line has only 6 parameters but the rest get 7. This trick ensures that the no arguments situation worksSalacious
@Eric - it's due to a bug in the microsoft compilers, but you can see this question for workarounds.Janinejanis
In your "Becomes..." explanation, this would be much clearer if you aligned the text by commas. Then it would be clearer why the 6th parameter is always going to be correct.Ineffective
This implementation does trigger a warning in a C99+ compliant compiler: warning: ISO C99 requires at least one argument for the "..." in a variadic macro. Which seems like a ridiculous and arbitrary requirement.Insessorial
in visual studio, I've got an error: Error C2059 syntax error: ',' . and there are warnings like: warning C4003: not enough arguments for function-like macro invocation 'XXX_4'Fruitage
The legacy trick used in this answer is GCC-specific. @aaron, and others, that's the reason of those errors/warnings.Trilinear
C
40

With greatest respect to Derek Ledbetter, David Sorkovsky, Syphorlate for their answers, together with the ingenious method to detect empty macro arguments by Jens Gustedt at

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

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

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:

#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_1 part comes from MACRO_CHOOSER(__VA_ARGS__), and the (10) part comes from the second (__VA_ARGS__).

The MACRO_CHOOSER uses the trick that, if __VA_ARGS__ is empty, the following expression is concatenated into a valid macro call by the preprocessor:

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 20 and 20 () are counted as 2 arguments for the bottom call respectively.

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_1 is a work-around for GCC/CLANG, suppressing a (false positive) error saying that ISO C99 requires rest arguments to be used when passing -pedantic to your compiler. The FUNC_RECOMPOSER is 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

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 example is here:

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

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

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

Corrianne answered 21/1, 2015 at 18:25 Comment(2)
How does one expand this to 3 or 4 functions?Bernard
@Bernard ideone.com/jD0Hm5 - zero to five arguments supported.Duplessismornay
S
33

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

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.

Stav answered 15/6, 2010 at 16:9 Comment(5)
Note that the reason why it's impossible to "overload" macros is because they don't have any inherent types. Macros are simply expanded.Herminiahermione
Although I use macros as little as possible, I found that debugging via trace output gets quite a bit easier with things like __FILE__ and __LINE__ and such...Liatris
not a good answer. this is a good answer : https://mcmap.net/q/67735/-can-c-c-preprocessor-macros-have-default-parameter-values-duplicate/893406Sidewalk
Conditional Compilation and Debugging/Logging is the domain where macros are really handy and legitimate. Every serious programmer knows it. What is good practice is to get away from using macros for defining constants and doing some crazy C level coding stuff to create container templates. I wish C++ would add more features to the macros too. They are orthogonal to templates. Best of course would be codelets that allow me to add generators into the compiler for domain specific language (aspects).Rubbery
I also think this is not a good answer, because a macro is something completly other than any C++ language option, because it will be handled BEFORE the compiler. So you can do other things, and no compiler or linker must optimize the code, because maybe its not to optimize.Docia
V
9

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:

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

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
Velum answered 8/9, 2012 at 8:2 Comment(9)
I'm getting unresolved external symbol _bool referenced in function _mainStesha
yes that can happen in some cases. you need to be aware that bool(#__VA_ARGS__) ? is different than the other macros since it is being evaluated at run time. depending on your case you could omit that part of the code though.Velum
I actually ended up with pastebin.com/H3T75dcn which works perfectly (0 arguments too).Stesha
Thanks for the link, and yes you can do it using sizeof too but for me that didn't work in some cases but principle is the same(boolean evaluation).Velum
Could you give some examples where it fails?Stesha
Sorry, didn't see your response. sizeof()/bool() can only be used within a function as part of regular code, the program has to be executed in order for this implementation to work. Macros are precompiler directives so it won't work for macros that require VA_NARGS as input for another precompiler macro.Velum
For example choosing a different macro for a macro with zero parameters won't work that way.Velum
This solution is almost perfect for what I'm doing, but is there any way to avoid triggering warning 4002 and 4003? I'm using MSVS2012; I tried inline warning suppression __pragmas, but it doesn't seem to want to work.Filament
@Avidanborisov, you are using bool() in C. This is C++ code and bool() is just a cast resolved at compile time.Scientistic
R
8

As a big fan of horrible macro monsters, I wanted to expand on Jason Deng's answer and make it actually usable. (For better or worse.) The original is not very nice to use because you need to modify the big alphabet soup every time you want to make a new macro and it's even worse if you need different amount of arguments.

So I made a version with these features:

  • 0 argument case works
  • 1 to 16 arguments without any modifications to the messy part
  • Easy to write more macro functions
  • Tested in gcc 10, clang 9, Visual Studio 2017

Currently I just made 16 argument maximum, but if you need more (really now? you're just getting silly...) you can edit FUNC_CHOOSER and CHOOSE_FROM_ARG_COUNT, then add some commas to NO_ARG_EXPANDER.

Please see Jason Deng's excellent answer for more details on the implementation, but I'll just put the code here:

#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;
}
Redress answered 8/5, 2019 at 10:30 Comment(0)
M
7

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

Mahoney answered 15/6, 2010 at 16:8 Comment(1)
They're standard in C99, and they're being added to C++0x as well.Randers
F
6

More concise version of Derek Ledbetter's code:

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;
}
Fcc answered 2/10, 2014 at 4:31 Comment(0)
M
5
#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: Mostly harmless.

Mecke answered 24/8, 2010 at 15:37 Comment(3)
there is an error in your code. please do :%s/MY_MACRO_/THINK_/g:)Felic
also, it didn't work with zero arguments using g++ i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)Felic
Zero arguments don't exists for variadiac macros, since the empty token is a valid placeholder.Booth
I
4

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

Inhabited answered 15/6, 2010 at 16:12 Comment(0)
A
4
#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.

Ara answered 15/6, 2010 at 16:14 Comment(1)
I was actually asking about the existence of the feature.Manilla
S
2

You can use BOOST_PP_OVERLOAD from a boost library.

Example from 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
Shamrock answered 22/2, 2018 at 9:16 Comment(0)
S
0

Depending on what you need, you could do it with var args with macros. Now, optional parameters or macro overloading, there is no such thing.

Ss answered 15/6, 2010 at 16:10 Comment(0)
K
0

Not directly answering the question, but using the same trick as David Sorkovsky answer and giving a clear example of how to build complex macros.

Just compile this with 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);

To see the output:

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

Now, a full working program would be:

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

Which would output the following by being compiled with 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

Note: It is important to use () around the macro contents as #define GET_FIRST_ARG_1_ARGS(default, a) (a) to not break in ambiguous expressions when a is just not a integer.

Extra macro for second argument:

#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__), \
    )
Kern answered 12/9, 2022 at 19:47 Comment(0)
C
0

Here is what I wrote in my cpp 20 testing framework: https://github.com/yhyu13/HLVM-Engine/blob/main/Engine/Source/Common/Test/Test.h

I used if constexpr (ArgCount == 1) to determine the number of arguments and corresponding macro to call

Test cases defined as

RECORD(dummy_test)
{
    // Dummy
}

RECORD(dummy2_test, false)
{
    // Dummy2
}

would both works

template <typename... TArgs>
constexpr size_t ct_GetArgsCount(TArgs&&...)
{
    return sizeof...(TArgs);
}

#define HLVM_GET_ARGS_COUNT(...) \
    ct_GetArgsCount(__VA_ARGS__)

struct AutoRegisterContext
{
    bool bEnabled = true;
};
/** Macro to record a test function
 * Requirement : (1) use static method in a .cpp test file
 *               (2) function name prefix "test_"
 * static bool test_hash_test()
 * {
 *     ...
 * };
 * RECORD_TEST_FUNC(hash_test);
 */
#define RECORD_TEST_FUNC_BODY(test_function)                                                                \
    struct AutoRegister                                                                                     \
    {                                                                                                       \
        AutoRegister()                                                                                      \
        {                                                                                                   \
            if (_AutoRegisterContext.bEnabled)                                                              \
                recorded_test_functions.push_back(make_test_wrapper(#test_function, test_##test_function)); \
        }                                                                                                   \
    };                                                                                                      \
    static AutoRegister _AutoRegister

#define RECORD_TEST_FUNC1(test_function, ...)                         \
    HLVM_STATIC_VAR AutoRegisterContext _AutoRegisterContext{ true }; \
    RECORD_TEST_FUNC_BODY(test_function);

#define RECORD_TEST_FUNC2(test_function, ...)                                \
    HLVM_STATIC_VAR AutoRegisterContext _AutoRegisterContext{ __VA_ARGS__ }; \
    RECORD_TEST_FUNC_BODY(test_function);

#define RECORD_TEST_FUNC(test_function, ...)                                          \
    namespace record_##test_function                                                  \
    {                                                                                 \
        constexpr auto ArgCount = HLVM_GET_ARGS_COUNT(#test_function, ##__VA_ARGS__); \
        struct RecordTestFunc                                                         \
        {                                                                             \
            RecordTestFunc()                                                          \
            {                                                                         \
                if constexpr (ArgCount == 1)                                          \
                {                                                                     \
                    RECORD_TEST_FUNC1(test_function, ##__VA_ARGS__);                  \
                }                                                                     \
                if constexpr (ArgCount == 2)                                          \
                {                                                                     \
                    RECORD_TEST_FUNC2(test_function, ##__VA_ARGS__);                  \
                }                                                                     \
            }                                                                         \
        };                                                                            \
        static RecordTestFunc _RecordTestFunc;                                        \
    }

/**
 * Macro to record a test function (easier version)
 * Example :
 * RECORD(hash_test)
 * {
 *    ...
 * }
 */
#define RECORD(test_function, ...)                  \
    void test_##test_function();                    \
    RECORD_TEST_FUNC(test_function, ##__VA_ARGS__); \
    void test_##test_function()
Chlodwig answered 23/3 at 16:43 Comment(0)
S
-1

None of the above examples (from Derek Ledbetter, David Sorkovsky, and Joe D) to count arguments with macros worked for me using Microsoft VCC 10. The __VA_ARGS__ argument is always considered as a single argument (token-izing it with ## or not), so the argument shifting in which those examples rely doesn't work.

So, short answer, as stated by many others above: no, you can't overload macros or use optional arguments on them.

Scientist answered 30/3, 2012 at 19:41 Comment(1)
You can, but only in C99 or C++11 (due to having __VA_ARGS__). VC2010 is C89/C++03 (with some bits of C++11 starting to turn up, but not that one yet).Vestibule

© 2022 - 2024 — McMap. All rights reserved.