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
The C language contains the typedef keyword to allow users to provide alternative names for the primitive (e.g., int) and user-defined (e.g struct) data types. Remember, this keyword adds a new name for some existing data type but does not create a new type.
Basically struct is used to define a structure. But when we want to use it we have to use the struct keyword in C. If we use the typedef keyword, then a new name, we can use the struct by that name, without writing the struct keyword.
typedef is a predefined keyword. You can replace the name of the existing data type with the name which you have provided. This keyword helps in creating a user-defined name for an existing data type. You can also use the typedef keyword with structures, pointers, arrays etc.
typedef is a reserved keyword in the programming languages C and C++. It is used to create an additional name (alias) for another data type, but does not create a new type, except in the obscure case of a qualified typedef of an array type where the typedef qualifiers are transferred to the array element type.
Such a declaration is most convenient if you learned C++ first, where you may omit the struct keyword if the name is not ambiguous. typedef names for structs could be in conflict with other identifiers of other parts of the program.
Using ‘typedef’, we cannot create a new datatype but define a new name for already existing type. This statement tells the compiler to recognize ‘bhanu’ as another name for ‘int’. ‘bhanu’ is used to create another variable ‘a’ . ‘bhanu a ‘declares ‘a’ as a variable of type ‘int’.
This is very used for example when working with linked lists in C The new defined type can be used just as other basic types for almost everything. Try for example to create an array of type student and see how it works. Structs can be copied or assigned but you can not compare them!
typedef struct Point Point; struct Point { int x, y; }; to have advantage of both possible definitions of point. Such a declaration is most convenient if you learned C++ first, where you may omit the struct keyword if the name is not ambiguous. typedef names for structs could be in conflict with other identifiers of other parts of the program.
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)
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) \
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With