I have many macros that end up generating code. For example:
#define CODE_GEN_IDENT1_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT1_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
The idea is that HDR generates functions definitions and SRC generates their implementation. All macros have the same amount of arguments (in this example, 3). IDENT can be any name like MATH, TRIG, ALGS, CONTAINERS, etc. This is what I want to focus on.
I'm trying to build up a macro that can generate all of these macros macros with different IDENT using variadic macros. For example:
// Concatenate macros to a standard form
#define CONCATH_(C) CODE_GEN_##C##_HDR
#define CONCATC_(C) CODE_GEN_##C##_SRC
// CONCATH concatenates to HDR
// CONCATC concatenates to SRC
#define CONCATH(C) CONCATH_(C)
#define CONCATC(C) CONCATC_(C)
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
// Code that generates all other macros
// using CONCATH and CONCATC
// how could this be done?
When I write:
MASTER_MACRO(int, "Hello", char *, MATH, TRIG, CONT)
I would like to have something like:
CODE_GEN_MATH_HDR(int, "Hello", char *)
CODE_GEN_TRIG_HDR(int, "Hello", char *)
CODE_GEN_CONT_HDR(int, "Hello", char *)
CODE_GEN_MATH_SRC(int, "Hello", char *)
CODE_GEN_TRIG_SRC(int, "Hello", char *)
CODE_GEN_CONT_SRC(int, "Hello", char *)
To somehow access the given arguments and concatenate each, making both header and source.
What I currently have is a fixed length macro like:
MASTER_MACRO(PARAM1, PARAM2, PARAM3, MATH, TRIG, CONT, DUPL, SORT) \
CONCATH(MATH)(PARAM1, PARAM2, PARAM3)
CONCATH(TRIG)(PARAM1, PARAM2, PARAM3)
...
CONCATC(MATH)(PARAM1, PARAM2, PARAM3)
...
And when the user doesn't want to generate CONT, DUPL or any other macro he's got to pass in a pre-defined argumet like _ meaning he is not wanting to generate that and having an empty macro:
#define CODE_GEN___SRC(PARAM1, PARAM2, PARAM3) // Empty
But this is not good enough. On different projects these different IDENT have different names, so having to make new master macros are a bit annoying.
But the big questions are:
Can this be done?
Is there a simple way to do this?
Can it be done using only standard C (no compiler-dependent macro)?
Yes, you can do this. For a hand rolled implementation, you probably want to start with a basic argument counter like this:
#define COUNT(...) \
COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(_ ,_9,_8,_7,_6,_5,_4,_3,_2, X,...) X
The argument counter works kind of like a "shift register", injecting the argument list before the count. If called with one argument, everything aligns X with 1. Each additional argument shifts this list over... 2 arguments shifts 2 into X, 3 shifts 3 in, and so on. This is just the basic form, supporting up to 9 arguments, to convey the idea.
...now you can generate variadic macro utilities like this one:
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define TRANSFORM_CD(MACRO, ...) GLUE(TRANSFORM_CD_,COUNT(__VA_ARGS__))(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_1(MACRO,X) MACRO(X)
#define TRANSFORM_CD_2(MACRO,X,...) MACRO(X),TRANSFORM_CD_1(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_3(MACRO,X,...) MACRO(X),TRANSFORM_CD_2(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_4(MACRO,X,...) MACRO(X),TRANSFORM_CD_3(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_5(MACRO,X,...) MACRO(X),TRANSFORM_CD_4(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_6(MACRO,X,...) MACRO(X),TRANSFORM_CD_5(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_7(MACRO,X,...) MACRO(X),TRANSFORM_CD_6(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_8(MACRO,X,...) MACRO(X),TRANSFORM_CD_7(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_9(MACRO,X,...) MACRO(X),TRANSFORM_CD_8(MACRO,__VA_ARGS__)
Conceptually TRANSFORM_CD is meant to "transform" a Comma Delimited list (args 2 and up) to another comma delimited list by applying a macro to it. In this case, we start with a comma delimited list of base names (what you call IDENT here) and apply one of your transform macros to it; for example, TRANSFORM(CONCATH, TRIG, CONT, DUPL) expands to CODE_GEN_TRIG_HDR, CODE_GEN_CONT_HDR, CODE_GEN_DUPL_HDR.
Next we need something to generate calls with multiple macros and the same parameter set; like this utility:
#define INVOKE_ALL(TUPLE_, ...) GLUE(INVOKE_ALL_,COUNT(__VA_ARGS__))(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_1(TUPLE_, X) X TUPLE_
#define INVOKE_ALL_2(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_1(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_3(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_2(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_4(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_3(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_5(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_4(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_6(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_5(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_7(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_6(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_8(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_7(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_9(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_8(TUPLE_,__VA_ARGS__)
Here, TUPLE_ is meant to be a parenthesized argument list (this makes the utility a bit more generic than requiring it support exactly three parameters); and each other parameter represents a macro to be called with those arguments.
Combining the two, this:
INVOKE_ALL((p1 a1, p2 a2, p3 a3),TRANSFORM_CD(CONCATH,MATH,TRIG,CONT,DUPL))
...should expand to (reformatted for clarity):
CODE_GEN_TRIG_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_CONT_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_DUPL_HDR(p1 a1, p2 a2, p3 a3)
...and if you really want, you could preserve MASTER_MACRO's form and function, simply making it variadic, like this:
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATH,__VA_ARGS__)) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATC,__VA_ARGS__))
demo at stacked-crooked
I wish to determine the types of the parameters passed to a function using VA_ARGS in order to route it to the right handler, but in compile time (and not inside a function with va_args()).
by determine type i mean i need to know if the trace contains only integers or has strings in it as well, but i wish it will be in compile time.
for example:
#define TRACE_HANDLER(type_branch) (Invoke_ ## type_branch)
#define TYPE_ARGS(args) ______//Determine if all arguments are uint32________
#define TRACE_(formatString,...) TRACE_HANDLER(TYPE_ARGS(__VA_ARGS__))(__VA_ARGS__)
#define TRACE(Id,formatString,...) TRACE_(formatString,__VA_ARGS__)
any ideas?
thanks!
You can do a compile-time dispatch on the type of an expression with the _Generic operator. Note that this is part of the main C language, not preprocessor macros.
int x = 0;
_Generic(x, int: invoke_int,
float: invoke_float,
double: invoke_double)(x); //calls invoke_int with x
The expression you give as the first argument to _Generic is only used for its type, at compile-time, to select a value to inline (in this case, a function to pass the runtime variable).
_Generic is only intended for use with a single parameter, and as a consequence most examples only show how to overload functions with a single argument. You could engage in some hefty metaprogramming to create deeply-nested _Generic trees that chew their way through all passed arguments, but here's one much simpler possible way to overload a function with multiple arguments of multiple types:
#include <stdlib.h>
#include <stdio.h>
// specialized definitions
void overload_1(int a, int b, int c) {
printf("all ints (%d, %d, %d)\n", a, b, c);
}
void overload_2(int a, char * b, int c) {
printf("b is a string (%d, %s, %d)\n", a, b, c);
}
void overload_3(char * a, int b, char * c) {
printf("a and c are strings (%s, %d, %s)\n", a, b, c);
}
void static_error(int l) { printf("error with overload on %d\n", l); exit(1); }
// type indices
enum ARG_TYPE {
INT = 0, CHAR_P
};
// get the ID of a specialization by the list of arg types
static inline int get_overload_id(int ac, int av[]) {
return (ac == 3 && av[0] == INT && av[1] == INT && av[2] == INT) ? 1
: (ac == 3 && av[0] == INT && av[1] == CHAR_P && av[2] == INT) ? 2
: (ac == 3 && av[0] == CHAR_P && av[1] == INT && av[2] == CHAR_P) ? 3
: -1 //error
;
}
// overloaded definition
#define overload(...) overload_ex(get_overload_id(M_NARGS(__VA_ARGS__), (int[]){ M_FOR_EACH(GET_ARG_TYPE, __VA_ARGS__) }), __VA_ARGS__)
#define overload_ex(getID, ...) \
((getID == 1) ? overload_1(GET_ARG(0, INT, __VA_ARGS__), GET_ARG(1, INT, __VA_ARGS__), GET_ARG(2, INT, __VA_ARGS__)) \
:(getID == 2) ? overload_2(GET_ARG(0, INT, __VA_ARGS__), GET_ARG(1, CHAR_P, __VA_ARGS__), GET_ARG(2, INT, __VA_ARGS__)) \
:(getID == 3) ? overload_3(GET_ARG(0, CHAR_P, __VA_ARGS__), GET_ARG(1, INT, __VA_ARGS__), GET_ARG(2, CHAR_P, __VA_ARGS__)) \
:static_error(__LINE__))
#define GET_ARG_TYPE(A) _Generic(((void)0, (A)), int: INT, char*: CHAR_P),
#define GET_ARG(N, T, ...) GET_ARG_DEFAULT_##T(M_GET_ELEM(N, __VA_ARGS__,0,0,0,0,0,0,0,0,0,0,0,0,0))
#define GET_ARG_DEFAULT_INT(A) _Generic((A), int: (A), default: 0)
#define GET_ARG_DEFAULT_CHAR_P(A) _Generic(((void)0, (A)), char*: (A), default: NULL)
// metaprogramming utility macros (not directly related to this
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)
#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
#define M_GET_ELEM(N, ...) M_CONC(M_GET_ELEM_, N)(__VA_ARGS__)
#define M_GET_ELEM_0(_0, ...) _0
#define M_GET_ELEM_1(_0, _1, ...) _1
#define M_GET_ELEM_2(_0, _1, _2, ...) _2
#define M_GET_ELEM_3(_0, _1, _2, _3, ...) _3
#define M_GET_ELEM_4(_0, _1, _2, _3, _4, ...) _4
#define M_GET_ELEM_5(_0, _1, _2, _3, _4, _5, ...) _5
// (end of utility stuff)
int main(void) {
overload(1, 2, 3); // prints "all ints (1, 2, 3)"
overload(1, "two", 3); // prints "b is a string (1, two, 3)"
overload("one", 2, "three"); // prints "a and c are strings (one, 2, three)"
}
(M_NARGS, M_FOR_EACH and M_GET_ELEM are utility macros... you can extend them for more arguments easily, but they aren't directly connected to this.)
The way this works is to build a big ternary-operator conditional expression that contains all possible specializations for the function. We use the GET_ARG macro for each argument passed to a specialization, to choose using _Generic whether to supply an actual argument (if it's the right type for this branch), or a suitable default replacement (if this is the wrong one, in which case it will just go unused). _Generic is also mapped over all arguments using M_FOR_EACH to build a "runtime" array of type-id integers. This array, plus the number of arguments, is passed to get_overload_id to get the integer ID of the function we actually want to call, for use as a controlling expression in the big ternary expression.
Despite using runtime-level C constructs (a big ternary with all variations, a dispatch function to control it), this actually doesn't have any real runtime cost: since the arguments to the dispatch function are constant and it itself is static inline, GCC (and presumably any other half-decent compiler) can completely inline it and optimise out all of the unused branches of the big ternary, leaving only the specialization we actually want in the generated assembly (you can compile with gcc -S and see that this is the case). It is effectively a completely compile-time operation.
There is no way to do this. By the time preprocessor does the macro expansion, all parameters are treated as text. Compiler hasn't even started analyzing the C code, so types don't even exist yet.
Only way to make it work is to use explicit type parameter:
#define TRACE(Id, type_branch, formatString,...)
Is there some way of getting optional parameters with C++ Macros? Some sort of overloading would be nice too.
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.
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)!
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
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 =)
Works for zero argument, as well as unlimited number of arguments, if you expand it further to suit your needs
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! =)
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.
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
gcc/g++ supports varargs macros but I don't think this is standard, so use it at your own risk.
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;
}
#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.
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;
}
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++)!
#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.
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
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.
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__), \
)
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.