Reputation: 51
Is it possible get variable type which is inside union? For example I have union:
typedef struct A {
union {
int p;
double d;
} pointer;
int a;
struct A *next;
} myStruct;
and I have function:
myStruct *getMinInList(myStruct *root) {
// do some things to get the smallest value in List,
// simple and I don't write it here because it's not a point
return smallest;
}
sometimes in union is integer or could be double. Is it possible to get information which is used? Or I have to write two functions: one to get min element in list with integer and second for get min element in list - double?
Upvotes: 2
Views: 214
Reputation: 144740
The union
does not intrinsically store any extra information such as which member was last written. Unless context makes it obvious, you should have another member in the struct
that specifies which member of the union
is used. This may be the purpose of a
in your example, but a member named type
defined as an explicit enum
would make the semantics more obvious (this type of enum
is called a tagged enum). Your function then needs to check for each node which member to use for the minimum computation.
With an extra type
member, the code would look like:
typedef struct A {
union {
int p;
double d;
} pointer;
enum { POINTER_INT = 0, POINTER_DOUBLE } type;
int a;
struct A *next;
} myStruct;
static double get_value(const myStruct *x) {
return x->type == POINTER_DOUBLE ? x->pointer.d : (double)x->pointer.p;
}
myStruct *getMinInList(myStruct *root) {
myStruct *smallest = root;
while (root) {
if (get_value(root) < get_value(smallest)) {
smallest = root;
}
root = root->next;
}
return smallest;
}
If a
has a different purpose, add a type
member with the above semantics.
NOTE: this quick and dirty solution makes the assumption that all values of type int
can be accurately represented in type double
. This would not be the case if both int
and double
are 64 bits. In this case, it is quite cumbersome to compare large integers and doubles that are close to them because the conversion from int
to double
truncates the low order bits. Such truncation can be detected by testing if (int)(double)root->pointer.p == root->pointer.p
. Here is a more elaborate solution for this case, that could be easily adapted any longer integer type:
static double get_value(const myStruct *x, int *adjust) {
if (x->type == POINTER_DOUBLE) {
*adjust = 0;
return x->pointer.d;
} else {
double d = (double)s->pointer.p;
*adjust = x->pointer.p - (int)d;
return d;
}
}
myStruct *getMinInList(myStruct *root) {
myStruct *smallest = root;
while (root) {
int adjust1, adjust2;
double val1 = get_value(root, &adjust1);
double val2 = get_value(smallest, &adjust2);
if (val1 < val2 || (val1 == val2 && adjust1 < adjust2)) {
smallest = root;
}
root = root->next;
}
return smallest;
}
Upvotes: 4
Reputation: 37834
You don't just want a union, you want a tagged union
Use a tag(possibly an enum) to describe what value the union is currently holding.
typedef struct A
union {
int p;
double d;
} pointer;
//remember which field was last written
enum { P_ACTIVE = 1, D_ACTIVE } tag;
} myStruct;
This kind of type is expressed more easily in other languages.
For example, in C++, the type you would probably want, is a variant.
Upvotes: 1