I would like to use _Generic to overload functions, one of them having no arguments, something like:
#include <stdio.h>
#include <string.h>
void f1(void)
{
printf("F1\n");
}
void f2(int n)
{
printf("F2 %d\n", n);
}
#define func(x) _Generic((x), \
int: f2, \
default: f1 \
)(x)
int main(void)
{
func(); // I want this to call f1
return 0;
}
Is this possible?
Due to a fundamental issue in C pre-processor, elegant dispatching between function-like macro with a single and no-parameter is not possible without extensions. The upcoming C23 standard will support __VA_OPT__
which injects an extra token when the __VA_ARGS__
is non-empty. Usually it is a extra comma. This feature is already supported by some compilers including GCC and CLANG.
You can use NoArg
trick as in the other answer but enhance it with __VA_OPT__
to support func()
nicely.
#include <stdio.h>
#include <string.h>
void f1(void) { puts("F1");}
void f2(int n) { printf("F2 %d\n", n); }
typedef struct NoArg NoArg;
#define func(...) func_(__VA_ARGS__ __VA_OPT__(,) (NoArg*)0, ~ )(__VA_ARGS__)
#define func_(x, ...) \
_Generic((x) \
, NoArg*: f1 \
, int: f2 \
)
int main(void) {
func();
func(5);
return 0;
}
Works as expected. See godbolt.
The tricky part is the expansion of func
macro to:
func_(__VA_ARGS__ __VA_OPT__(,) (NoArg*)0, ~ ) (__VA_ARGS__)
The tail of (__VA_ARGS__)
keeps the original arguments to be used after the func_
is expanded to _Generic
. The __VA_OPT__(,)
adds a comma if and only if the parameter list is empty. Next, the (NoArg*)0
indicator is passed. It will be the first parameter is original parameter list was empty. Finally, the parameters are finished with dummy ~
to make sure that func_
macro is evaluated with at least two tokens letting use func_(x, ...)
without issues with missing arguments.
There is a way to do it in C11. Just use compound literal of an array type.
Use (int[]) { 0, __VA_ARGS__ }
. Let empty list in func()
expand to (int[]) { 0, }
which is an array of type int[1]
while let 5
in func(5)
expands to (int[]) { 0, 5 }
which is an array of type int[2]
. Next take an address of such array to avoid "array decay" in _Generic
. Dispatch a proper function from the type of an array pointer.
#include <stdio.h>
#include <string.h>
void f1(void) { puts("F1");}
void f2(int n) { printf("F2 %d\n", n); }
#define func(...) func_((&(int[]){ 0, __VA_ARGS__})) (__VA_ARGS__)
#define func_(x) \
_Generic((x) \
, int(*)[1]: f1 \
, int(*)[2]: f2 \
)
int main(void) {
func(); // This will call f1
func(5); // This will call f2 with argument of 5
return 0;
}
It compiles in a pedantic mode with no warning and it produces the expected output. See godbolt. The main disadvantage it that it can work only for integer parameters.
As pointed by a comment from Lundin, there is even a simpler version that works for functions with any type.
#define func(...) \
_Generic (& # __VA_ARGS__ \
, char(*)[1]: f1 \
, default: f2 \
)(__VA_ARGS__)
The trick is stringification of parameter list. The empty one will be transformed to ""
. The extra spaces are removed as pointed in 6.10.3.2p2:
... White space before the first preprocessing token and after the last preprocessing token composing the argument is deleted. ...
The interesting thing about string literal is that they are technically L-values so one can take their address! It allows to dispatch expression from the type of the array pointer which is char(*)[1]
for &""
.