user189728
user189728

Reputation: 288

Optimizing tagged unions in C

I'm using tagged unions in a personal project. As an example, this is the type of code I use:

struct data_unit {
  enum {
    d_A, d_B, d_C
  } dtype;
  union {
    char a;
    int b;
    double c;
  } data;
};

void print_data(data_unit du) {
  switch (du.dtype) {
    case d_A: {
      printf("%c", du.data.a);
      break;
    }
    case d_B: {
      printf("%d", du.data.b);
      break;
    }
    case d_C: {
      printf("%lf", du.data.c);
      break;
    }
    default:
      break;
  }
  return;
};

Is there more efficient way to implement print_data, one without manually checking each case of dtype? If C allowed lists of types, I would just iterate through it using the enum, but that isn't an available utility. I need a general way to do this, as I plan on adding a lot of data types to the union.

Is this possible to do? If not, what would a better strategy be for me in this context?

Upvotes: 3

Views: 1496

Answers (1)

Stephan Lechner
Stephan Lechner

Reputation: 35164

You could make use of the fact that a pointer to a struct-object may be treated as a pointer to its first member. Hence, when exchanging dtype and data, you could access all members in some generic way:

struct data_unit {
    union {
        char a;
        int b;
        double c;
    } data;
    enum {
        d_A, d_B, d_C
    } dtype;
};

typedef void (print_data_unit) (struct data_unit *du);

void print_data_unit_char(struct data_unit *du) {
    printf("%c\n", *((char*)(du)));
}
void print_data_unit_double(struct data_unit *du) {
    printf("%lf\n", *((double*)(du)));
}
void print_data_unit_int(struct data_unit *du) {
    printf("%d\n", *((int*)(du)));
}

static print_data_unit* functions[3] = { print_data_unit_char, print_data_unit_int, print_data_unit_double };

void print_data(struct data_unit du) {
    functions[du.dtype](&du);
}

int main() {

    struct data_unit du;

    du.dtype = 0;
    du.data.a = 'c';
    print_data(du);

    du.dtype = 1;
    du.data.b = 100;
    print_data(du);

    du.dtype = 2;
    du.data.c = 200.55;
    print_data(du);
}

Upvotes: 2

Related Questions