I'm new to C and currently exploring the concept of function overloading via the use of _Generic in C11.
In this SO post, @Lundin provides the following code snippet, which I've gotten running with no issues:
#include <stdio.h>
void func_int (int x) { printf("%d\n", x); }
void func_char (char ch) { printf("%c\n", ch); }
#define func(param) \
_Generic((param), \
int: func_int(param), \
char: func_char(param)); \
int main()
{
func(1);
func((char){'A'});
}
@Lundin also mentioned the following:
A much better way would be to make a function interface with a single struct parameter, which can be adapted to contain all the necessary parameters. With such an interface you can use the above simple method based on _Generic. Type safe and maintainable.
So I decided to extend the above example to structs
to see it in action.
Below is my attempt:
#include <stdlib.h>
#include <stdio.h>
typedef struct args_int Args_int;
typedef struct args_char Args_char;
void func_int(Args_int args);
void func_char(Args_char args);
struct args_int {
int val;
};
struct args_char {
char val;
};
#define func(param)\
_Generic((param),\
Args_int: func_int(param),\
Args_char: func_char(param));
void func_int (Args_int args) {
printf("%d\n", args.val);
}
void func_char (Args_char args) {
printf("%c\n", args.val);
}
int main(void) {
Args_char args = {0};
args.val = 'A';
func(args);
}
However, unfortunately, I get the following compilation error, which complains that I've passed in an Args_char
when the compiler is expecting an Args_int
. Clearly, my intent is to pass an Args_char
and my expectation is for func_char
to be called as a result.
struct_args_by_val_executable.c:35:10: error: passing 'Args_char' (aka 'struct args_char') to parameter of incompatible type 'Args_int' (aka 'struct args_int')
func(args);
^~~~
struct_args_by_val_executable.c:20:28: note: expanded from macro 'func'
Args_int: func_int(param),\
^~~~~
struct_args_by_val_executable.c:24:25: note: passing argument to parameter 'args' here
void func_int (Args_int args) {
^
1 error generated.
Why isn't my example working as expected and what is the fix here?
On a related note, I managed to get a "pointer to a struct" version working without any issues as shown below. However, given the above compilation error, I feel as though this may have been a fluke?
#include <stdio.h>
typedef struct args_int Args_int;
typedef struct args_char Args_char;
void func_int(Args_int *args);
void func_char(Args_char *args);
struct args_int {
int val;
};
struct args_char {
char val;
};
#define func(param)\
_Generic((param),\
Args_int*: func_int(param),\
Args_char*: func_char(param));
void func_int (Args_int *args) {
printf("%d\n", args->val);
}
void func_char (Args_char *args) {
printf("%c\n", args->val);
}
int main(void) {
Args_char args = {0};
args.val = 'A';
func(&args);
}
The output to the above is as expected:
A
CodePudding user response:
The problem is that all expressions used in _Generic
must be valid.
In your example macro func
expands to:
int main(void) {
Args_char args = {0};
args.val = 'A';
_Generic(args,
Args_int: func_int(args),
Args_char: func_char(args));
}
Note that using func_int(args)
for args
which is Args_char
is causing an error.
The solution to that is using _Generic
to select a function pointer, next apply arguments to it.
#define func(param) \
_Generic((param), \
Args_int: func_int, \
Args_char: func_char) (param)
CodePudding user response:
Just posting this "for the record":
That's actually not a very good code example of me that you've found there! It's much better to include the parameter list outside the _Generic
clause just as @tstanisl just mentioned in their answer (I'd accept that one as the answer to your question). That's how the C committee intended the feature to be used even, you can find such examples in the standard itself, see for example 6.5.1:
#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)
The code snippet you found works by luck since char
and int
can be converted to each other when calling a function "as if by assignment". When using two non-compatible types that cannot get implicitly converted, the macro would expand incorrectly for the wrong type.
I've now updated https://stackoverflow.com/a/44633838/584518 to use this:
#define func(param) \
_Generic((param), \
int: func_int, \
char: func_char)(param) \