7

Having limited experience with unions in C++, I am struggling to understand the basic mechanics of casting to/from that datatype.

Suppose two types Type_a and Type_b are wrappers of type int and long respectively.

A union is defined as:

union Type_u {
    Type_a a;
    Type_b b;
}

Now I have something of type Type_a, lets call it j (Just to be confusing). I need to pass it to a function that expects a parameter of type Type_u:

void burninate(Type_u peasants);

What is the proper way to pass this variable j to burninate? (I've run into issues casting j to Type_u as well as passing it in as-is. Neither compiled.)

It may be worth pointing out that I have no ability to modify the union type (or Type_a or Type_b or burninate's signature for that matter.)

BlackVegetable
  • 12,594
  • 8
  • 50
  • 82
  • Are you asking about C or C++? They are like English and American - superficially "the same", except they really are not. You have both tags on the question, and start out by talking about C, them say it's a C++ project. Please pick just one. – Floris Nov 09 '13 at 03:41
  • @Floris Please excuse me for the confusion. The vast majority of my project is using conventions found in C, but the project is technically C++. Should I remove the C tag? – BlackVegetable Nov 09 '13 at 03:47
  • @Floris Corrected. Thank you for bringing that to my attention. I wasn't sure if C and C++ treated unions differently as I have had very little exposure to them in the past. – BlackVegetable Nov 09 '13 at 04:07
  • Thanks for fixing the tags. It still leaves one with the unanswerable question... "Why"??? Why a union of `long` and `int`??? There are so many ways that can go wrong (and how it goes wrong will be platform dependent). It is interesting, but seems to me to be very dangerous. – Floris Nov 09 '13 at 04:57
  • @Floris The actual example is using types that may be changed at a later point (theoretically). I think they are both actually the same type at the moment, but I wanted to see if I learned something additional if I proposed the (potential but unlikely) case where they were different sizes. – BlackVegetable Nov 09 '13 at 05:02
  • http://stackoverflow.com/questions/6352199/memory-layout-of-union-of-different-sized-member is somewhat informative in this regard. – Floris Nov 09 '13 at 05:05

4 Answers4

9

Since Type_a is the first element, you can initialize a union like this:

Type_a j;
Type_u u = {j};
burninate(u);

If you want to pass a type of Type_b:

Type_b k;
Type_u u;
u.b = k;
burninate(u);

There is a difference on this in C and C++. In C99, you can use designated initializer to initialize elements that isn't the first.

Type_b k;
Type_u u = {.b = k};
burninate(u);
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
4

You can pass j just like this ((Type_u)j) and it will work.

[EDIT] As the guys showed disbelief, here is the code sample Try to compile this code, it will work like a charm.

#include <stdio.h>
typedef union type_u {
    int a;
    long b;
}type;

int main ()
{
type T;
int j = 10;

    T = ((type)j);

    printf ("T.a:%d T.b:%lu \n", T.a, T.b);

return 0;
}

See the o/p XXX-mac:~ jork$ gcc union_test.c XXX-mac:~ jork$ ./a.out T.a:10 T.b:10

joe
  • 1,136
  • 9
  • 17
  • I swear, if it is that straightforward, I'm going to *downvote myself*. – BlackVegetable Nov 09 '13 at 02:42
  • 1
    @BlackVegetable I have added some code above. Just try to compile this code, it will work like a charm. It is that simple, don't confuse yourself :-) – joe Nov 09 '13 at 02:50
  • 1
    @joe: That's C, not C++. It won't work in C++, even with gcc. Casting to union is a gcc extension, so even the C code won't necessarily work on other compilers (although it works with clang). – rici Nov 09 '13 at 03:07
  • ah ok. I assumed C and C++ behaviors will be same. – joe Nov 09 '13 at 06:28
  • I think the confusion here was due to my erroneous tags earlier. This at least deserves an upvote (+1 from me.) – BlackVegetable Nov 09 '13 at 11:52
  • 1
    does not compile with Visual Studio – Joe McGrath Feb 04 '17 at 20:35
  • 1
    This answer confuses compilation with language features. You could improve this answer by stating what compiler you used, and with what flags (mainly std version). You should also tell whether this is standard conformant, unspecified or implementation defined behaviour. – pro-gramer May 07 '21 at 07:54
1

C++ does not generally go out of its way to encourage the use of unions, and it can be particularly awkward if union members have non-trivial constructors and destructors, since in that case the union's own constructor (destructor, respectively) will be deleted and you'll have to provide one yourself if you need one (which you generally do).

On the whole, it's probably not a great idea to put non-POD types into a union, since the semantics are awkward; in general, you cannot assign a value to an object which has not been constructed, and, while you could use placement new to construct a member of a union, you would have no way to actually know that the member hadn't been previously constructed. Furthermore, if a member has an explicit and non-trivial destructor, you could provide an explicit destructor for the union, which could explicitly call the member's destructor, but how would it know whether it had to do that or not?

If your union members are POD, though, you're fine, but you still cannot cast the union to a member or a member type to the union. (GCC allows this as a C extension, but afaik g++ does not extend C++ in the same way.)

Still, there is nothing stopping you from giving the union a constructor. For example:

union long_or_double {
  long a;
  double d;

  long_or_double(long l) : a(l) {}
  long_or_double(int i) : a(i) {}
  // etc.
  long_or_double(double d) : d(d) {}
};

int f(long_or_double u);

int main(int argc, char** argv) {
  // Both of these work, because there is an explicit constructor
  f(argc);
  f(3.7);
  // ...
}

If you cannot add constructors to the definition of the union type, I think the best you can do is to define a make_union function, overriding it appropriately for each member type.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Might want to update answer to reflect "long or int" as in original question (interesting since they are not the same size...) – Floris Nov 09 '13 at 03:44
  • @Floris: I really don't get the point of a union of `long` and `int`, with or without wrappers. Perhaps I should have used `type_a` and `type_b`, with a note that they are assumed to be POD types. – rici Nov 09 '13 at 03:47
  • While I actually found this answer to be more useful, I believe Yu Hao's answer fits the question more directly. – BlackVegetable Nov 09 '13 at 15:38
0

Unions work exactly like structs (the only difference is the way objects are allocated in memory). So you have Type_u U; Type_a j; U.a = j; burninate(U);

hdante
  • 7,685
  • 3
  • 31
  • 36