Home > database >  Convenience macro to select union member matching value type
Convenience macro to select union member matching value type

Time:02-01

I have a union type declared as follows:

typedef union Data {
    int i;
    char c;
    double d;
    float f;
} data;

I know fields are overwritten by new values. I would like to know if there is any way to do the following instead of needing to manually access each field depending on the type of data I want to store:

    data *c;
    *c = 3;  // instead of c.i = 3; c.i should be 3
    *c = 'a' // instead of c.c = 'a'; c.c should be 'a', c.i should no longer be 3;

I tried doing as written above, but I get an error saying:

Assigning to 'data' (aka 'union Data') from incompatible type 'int'.

Is there any way to do this?

CodePudding user response:

No. It's not possible. If you want type switching use _Generic but 'a' is an integer character constant (i.e. type is int) so you will only find partial success with sample input provided:

#include <stdio.h>

typedef union {
    char c;
    double d;
    float f;
    int i;
} data;

#define set(d, v) _Generic((v),\
    char: setc,\
    double: setd,\
    float: setf,\
    int: seti\
)((d), (v))

void setc(data d, int v) {
    d.c = v;
    printf("c = %c\n", d.c);
}

void setd(data d, double v) {
    d.d = v;
    printf("d = %lf\n", d.d);
}

void seti(data d, int v) {
    d.i = v;
    printf("i = %d\n", d.i);
}

void setf(data d, float f) {
    d.f = f;
    printf("f = %f\n", d.f);
}

int main() {
    data d = { 0 };
    set(d, 'a'); // seti()
    set(d, (char) 'c');
    set(d, 3.14);
    set(d, 1.2f);
    set(d, 3);
}

and the resulting output:

i = 97
c = c
d = 3.140000
f = 1.200000
i = 3

CodePudding user response:

Here is an alternative approach with a tagged union and a polymorphic instantiation macro. Note however that 'a' has type int in C and char in C , so it must be cast as (char)'a' to have type char in both languages.

#include <stdio.h>

typedef struct {
    enum { CHAR, INT, FLOAT, DOUBLE } type;
    union {
        char   c;
        int    i;
        float  f;
        double d;
    };
} data;

#define data(v) _Generic((v),   \
    char:   (data){ .type = CHAR,   .c = (v) }, \
    int:    (data){ .type = INT,    .i = (v) }, \
    float:  (data){ .type = FLOAT,  .f = (v) }, \
    double: (data){ .type = DOUBLE, .d = (v) })

void print(data x) {
    switch (x.type) {
    case CHAR:   printf("char:  '%c'\n", x.c); break;
    case INT:    printf("int:    %d\n", x.i); break;
    case FLOAT:  printf("float:  %gf\n", (double)x.f); break;
    case DOUBLE: printf("double: %g\n", x.d); break;
    }
}

int main() {
    data a = data((char)'a');  // char
    data b = data('a');        // int
    data c = data(3);          // int
    data d = data(1.2f);       // float
    data e = data(3.14);       // double
    print(a);
    print(b);
    print(c);
    print(d);
    print(e);
    return 0;
}

Output:

char:  'a'
int:    97
int:    3
float:  1.2f
double: 3.14
  •  Tags:  
  • c
  • Related