Uncle Jone
Uncle Jone

Reputation: 51

c union get type from it

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

Answers (2)

chqrlie
chqrlie

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

Trevor Hickey
Trevor Hickey

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

Related Questions