2

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?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Uncle Jone
  • 51
  • 3
  • 2
    Answer is _[HERE](http://stackoverflow.com/q/724617/645128)_ I believe. – ryyker Jan 17 '16 at 02:06
  • add a field, outside the union, that indicates what kind of data is inside the union. set that new field when setting the data inside the union, reference that field when determining what is in the union. – user3629249 Jan 17 '16 at 17:17

2 Answers2

4

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;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • @JonathanLeffler: I had not seen your post `;-)`. I am almost sure my *elaborate* version handles 64 bit ints correctly, do you agree? – chqrlie Jan 17 '16 at 03:03
  • You'd edited your post while I was typing mine — there's no problem at all. I think I'd cache the attributes of the current smallest value. You're right that if `sizeof(int) == sizeof(double)`, you have to worry about the `double` values corresponding to very large (positive or negative) integers. If `sizeof(double) != sizeof(long double)`, using `long double` might be a cop-out. I'm left wondering about your `adjust` values for large but negative integers, and the corresponding comparisons. I suppose I'd have to use `long` instead of `int` on a 64-bit machine to do the testing. – Jonathan Leffler Jan 17 '16 at 03:10
  • Using `long double` would work for the Intel architecture, but not for the DS9K. `adjust` should be negative or zero for large negative integers, but the lesser double would still indicate a lower value. There might also be an issue with NaN values: an initial NaN will be considered smallest, whereas subsequent ones will be skipped. – chqrlie Jan 17 '16 at 03:25
1

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.

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271