Home > Mobile >  How do you use _Generic with structs that are typedef-ed in C?
How do you use _Generic with structs that are typedef-ed in C?

Time:09-28

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)  \
  • Related