How to know which variable value is set for union?
Asked Answered
I

3

10

I am working on optimization of a project. It contains a struct of an options in which user can select a single option at a time. In addition to the option, we also use a flag variable to check which option value is set for this record. In order to make it memory efficient I want to convert struct into a union. But How do I know that which variable value is set in union. Because there is no restriction in union to fetch a value of a variable even which is not set.

 struct options{
     int basicPay;
     int lumsumPay;
     int mothlyPay;
     int weeklyPay;
     int dailyPay;
     int anualPay;

     int payType;   // Flag variable to check which option is selected
 };

union OptimizeOptions{
    int basicPay;
    int lumsumPay;
    int mothlyPay;
    int weeklyPay;
    int dailyPay;
    int anualPay;

    int payType;   // Confuse at here
 };
Ingeringersoll answered 10/10, 2014 at 10:21 Comment(1)
@user3121023 is right -- the only time you need a union is when one or more of the members have different types. Also spelling: lumpsumPay, monthlyPay and annualPay.Impeach
F
16

Have you tried a union inside of a struct? Let’s see the following example equivalent:

union options{
  int basicPay;
  int lumsumPay;
  int mothlyPay;
  int weeklyPay;
  int dailyPay;
  int anualPay;
};

struct record{
  union options op;   // Options union
  int payType;   // Flag variable to check which option is selected
}

The union (options) will reserve memory for its largest variable and you can set its value and your structure (record) will keep track of that union memory block and the payType flag value could be set which will tell your program to fetch the perticular variable of the union.

Fougere answered 10/10, 2014 at 10:25 Comment(2)
union options *op doesn't need to be a pointer and would make usage of struct record easier if it was union options op instead.Blockhouse
If all union options are of the same type int do you really need the union? struct with two ints should be enough.Batman
S
21

Unions suffers from a problem that there is no easy way to know which member of a union was last changed. In order to keep track of this information, you can embed union inside a structure that has one another member (called "tag field" or "discriminant"). The purpose of tag field is to remind which member is changed/updated. You can try this:

typedef struct{
       int payType;      // Tag field 

       union{
           int basicPay;
           int lumsumPay;
           int mothlyPay;
           int weeklyPay;
           int dailyPay;
           int anualPay;
      }OptimizeOptions;

}Options;

But, there is no need to write six separate members for union in your case as all are of int type. Therefore it can be reduced to

typedef struct{
    enum{BASIC_PAY, LUMSUM_PAY, MONTHLU_PAY, WEEKLY_PAY, DAILY_PAY, ANNUAL_PAY} payType; 
    int pay;
}Options;

Lets understand the use of tag field with a simple example. Suppose we want an array which can store int and double type data. This would become possible by using union. So, first define a union type which will store either int or double.

typedef union {
      int i;
      double d;
} Num;

Next we have to create an array whose elements are Num type

Num num_arr[100];

Now, suppose we want to assign element 0 of the num_arr to store 25, while element 1 stores 3.147. This can be done as

num_arr[0].i = 25;
num_arr[1].d = 3.147;  

Now suppose that we have to write a function which will print the num_arr elements. The function would be like this:

void print_num(Num n)
{
     if(n contains integer)
         printf("%d", n.i);
     else
         printf("%f", n.d);
}

Wait! How could print_num will decide whether n contains an integer or double?

This will be done by using the tag field:

typedef struct{
    enum{INT, DOUBLE} kind;        //Tag field
    union{
       int i;
       double d;
    }u;
}Num;   

So, each time a value is assigned to a member of u, kind must1 be set to either INT or DOUBLE to remind that what type we actually stored. For example:

n.u.i = 100;
n.kind = INT;

The print_num function would be like this:

void print_num(Num n)
{
     if(n.kind == INT)
         printf("%d", n.i);
     else
         printf("%f", n.d);
}  

1: It is programmer's responsibility to update the tag field with each assignment to the member of union. Forgetting to do so will lead to bug, as pointed in comment by @ j_random_hacker.

Sentient answered 10/10, 2014 at 10:23 Comment(5)
You forgot to declare a field of type union OptimizeOptions -- currently you're just declaring the union type itself, but no field of that type.Compony
I suggest changing "can" to "must" in "kind will be set to either INT or DOUBLE", to make it clear that this is something the programmer must do themselves; forgetting to do it will cause bugs.Impeach
@j_random_hacker; Agreed. Updated the answer.Sentient
It might be even better to put enum member, e.g. enum {INT, DOUBLE} kind; instead (implicitely with zero and one respectively), as in that way you can place possible choices directly into struct template and consequently not as some "loose" object-like macros beside it.Rumba
@GrzegorzSzpetkowski; First I thought the same. Latter I thought that OP may will confuse by seeing enum and union together inside a struct and may will fail to digest. That's why I used macro, but agreed with using enum.Sentient
F
16

Have you tried a union inside of a struct? Let’s see the following example equivalent:

union options{
  int basicPay;
  int lumsumPay;
  int mothlyPay;
  int weeklyPay;
  int dailyPay;
  int anualPay;
};

struct record{
  union options op;   // Options union
  int payType;   // Flag variable to check which option is selected
}

The union (options) will reserve memory for its largest variable and you can set its value and your structure (record) will keep track of that union memory block and the payType flag value could be set which will tell your program to fetch the perticular variable of the union.

Fougere answered 10/10, 2014 at 10:25 Comment(2)
union options *op doesn't need to be a pointer and would make usage of struct record easier if it was union options op instead.Blockhouse
If all union options are of the same type int do you really need the union? struct with two ints should be enough.Batman
G
0

There is now std::variant that wraps up this common functionality.

std::variant<int, float> x;
x = 42;
bool is_x_an_int = std::holds_alternative<int>(x); // YES
bool is_x_a_float = std::holds_alternative<float>(x); // NO
int value_of_x = std::get<int>(x); // 42
int an_error = std::get<float>(x); // exception

See https://en.cppreference.com/w/cpp/utility/variant.

Giannini answered 11/2, 2021 at 14:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.