How to check what type is currently used in union?
Asked Answered
E

7

50

let's say we have a union:

typedef union someunion {
    int a;
    double b;
} myunion;

Is it possible to check what type is in union after I set e.g. a=123? My approach is to add this union to some structure and set uniontype to 1 when it's int and 2 when it's double.

typedef struct somestruct {
    int uniontype
    myunion numbers;
} mystruct;

Is there any better solution?

Emsmus answered 18/5, 2013 at 10:28 Comment(0)
N
43

Is there any better solution?

No, the solution that you showed is the best (and the only) one. unions are pretty simplistic - they do not "track" what you've assigned to what. All they do is let you reuse the same memory range for all their members. They do not provide anything else beyond that, so enclosing them in a struct and using a "type" field for tracking is precisely the correct thing to do.

Nuclide answered 18/5, 2013 at 10:30 Comment(3)
A better solution is to use enum values rather than 1, 2, etc.Roborant
And actually, this technique is adopted very widely in kernel network programming, for the content of a received packet(datagram) is usually unknown before checking type code in header.Lentiginous
C: my blessing is my curseMonopoly
W
9

C does not automatically keep track of which field in a union is currently in use. (In fact, I believe reading from the "wrong" field results in implementation defined behavior.) As such, it is up to your code to keep track of which one is currently used / filled out.

Your approach to keeping a separate 'uniontype' variable is a very common approach to this, and should work well.

Womb answered 18/5, 2013 at 10:31 Comment(0)
P
5

There is no way to directly query the type currently stored in a union.

The only ways to know the type stored in a union are to have an explicit flag (as in your mystruct example), or to ensure that control only flows to certain parts of the code when the union has a known active element.

Polley answered 18/5, 2013 at 10:31 Comment(0)
Z
2

Depending on the application, if it is a short lived object you may be able to encode the type in the control flow, ie. have separate blocks/functions for both cases

  struct value {
      const char *name;
      myunion u;
  };

  void throwBall(Ball* ball)
  {
     ...
     struct value v;
     v.name = "Ball"; v.u.b = 1.2;
     process_value_double(&v);      //double
     struct value v2;
     v2.name = "Age";
     v2.u.a = 19;
     check_if_can_drive(&v2);       //int
     ...
  }

  void countOranges()
  {
       struct value v;
       v.name = "counter";
       v.u.a = ORANGE;
       count_objects(&v);          //int
  }
Zenithal answered 18/5, 2013 at 10:38 Comment(0)
T
2

Warning: the following is just for learning purpose:

You could use some ugly tricks to do so (as long as the data types in your union have different sizes, which is the present case):

#include <stdio.h>

typedef union someunion {
  int a;
  double b;
} myunion;

typedef struct somestruct {
  int uniontype;
  myunion numbers;
} mystruct;


#define UPDATE_CONTENT(container, value) if ( \
                                             ((sizeof(value) == sizeof(double)) \
                                              ? (container.uniontype = ((container.numbers.b = value), 2)) \
                                              : (container.uniontype = ((container.numbers.a = value), 1))))

int main()
{
  mystruct my_container;

  UPDATE_CONTENT(my_container, 42);
  printf("%d\n", my_container.uniontype);
  UPDATE_CONTENT(my_container, 37.1);
  printf("%d\n", my_container.uniontype);
  return (0);
}

But I advise you never do this.

Trachoma answered 18/5, 2013 at 10:55 Comment(0)
D
1

There isn't a direct way to inspect the type of data stored in the union. So, you are responsible for tracking the type in the union.

For a better readablity, using an Enum over int can be a good idea.

typedef union someunion {
    int a;
    double b;
} myunion;

typedef enum someEnum{
    INT,
    DOUBLE
} myuniontype;

typedef struct somestruct {
    myuniontype uniontype;
    myunion numbers;
} mystruct;

int main() {
    mystruct my_container;
    my_container.uniontype = myuniontype::INT;
    my_container.numbers.a=5;
    switch (my_container.uniontype) {
        case myuniontype::INT:
            std::cout<<my_container.numbers.a<<std::endl;
            break;
        case myuniontype::DOUBLE:
            std::cout<<my_container.numbers.b<<std::endl;
            break;
    }
    return 0;
}
Defraud answered 16/7, 2023 at 9:48 Comment(0)
K
0

Maybe my variant is helping

struct Table
{
    char mas[10];
    int width;
    int high;
    union stat
    {
        int st;
        char v;
    } un;
};


Table tble[2];
strcpy(tble[0].mas, "box");
tble[0].high = 12;
tble[0].width = 14;
tble[0].un.v = 'S';

strcpy(tble[1].mas, "bag");
tble[1].high = 12;
tble[1].width = 14;
tble[1].un.st = 40;

//struct Table *ptbl = &tble[0];
//ptbl++;

for (int i = 0; i < 2; i++)
{
    void *pt = &tble[i].un;
    if(*((char*)pt) == 'S')     
        sort(put_on_bag_line);
    else
        sort(put_on_box_line);
}
Kulseth answered 10/4, 2019 at 9:27 Comment(1)
What would happen if the number happened to be 83 (the ascii decimal for S)? And you're relying on knowing all the values it can hold in advance.Bloodline

© 2022 - 2024 — McMap. All rights reserved.