What would you use a union for? Is it just depricated idea? even with the color idea, a class/struct could easily do the same thing and probably better.
🔒 Locked
What are unions good for?
Advertisement
Original Post
Bored and reading in my favourite room I noticed out of all the c/c++ books I have only one even spends a paragraph talking about unions, and from the little reading here I understand what unions are and what they do... but other than for defining color values that can acces individual channels or the whole color I can't think what they would be good for.
What would you use a union for? Is it just depricated idea? even with the color idea, a class/struct could easily do the same thing and probably better.
What would you use a union for? Is it just depricated idea? even with the color idea, a class/struct could easily do the same thing and probably better.
Some form of event maybe with an "additional info" variable.
additionalInfo.Int = 10;
additionalInfo.Float = 10.0f
Obviously you can just cast but I believe you can also have type that take a larger number of bytes (such as a double) but use only a bit of it (unsigned int).
additionalIfno.Double = 1.0;
additionalInfo.UnsignedInt = 10;
double takes up more memory than an unsigned int
additionalInfo.Int = 10;
additionalInfo.Float = 10.0f
Obviously you can just cast but I believe you can also have type that take a larger number of bytes (such as a double) but use only a bit of it (unsigned int).
additionalIfno.Double = 1.0;
additionalInfo.UnsignedInt = 10;
double takes up more memory than an unsigned int
Some form of event maybe with an "additional info" variable.
additionalInfo.Int = 10;
additionalInfo.Float = 10.0f
Obviously you can just cast but I believe you can also have type that take a larger number of bytes (such as a double) but use only a bit of it (unsigned int).
additionalIfno.Double = 1.0;
additionalInfo.UnsignedInt = 10;
double takes up more memory than an unsigned int
I believe that is incorrect... the union should take up the space of the largest variable?
You can use unions for handy syntactic sugar purposes. Probably the most useful aspect of unions. For example:
This allows you to use this vector class with XYZW notation, or RGBA, or UVST, which is similar how you can access vector components in vertex/fragment shaders.
Another use of union notation is mapping larger data types over a collection of smaller types. For example,
You can access different parts of the integer without masking. Note that there are pitfalls with this, the mapping order of the array will be architecture dependent. In fact, you can use this type of union to test for machine endian-ness. For example:
You can map float data types across integers, use bit mask manipulation on the integer mapping to modify the float data.
I have used unions in a similar manner to convert exotic 80-bit long double types to 64-bit double using bit manipulation.
It's quite rare when you need to do something like this, and its best to avoid if you can.
class vec4
{
public:
union {float X, U, R;};
union {float Y, V, G;};
union {float Z, S, B;};
union {float W, T, A;};
};
vect4 Colour;
Colour.R = 1.0f; //Same as Colour.X = 1.0f;
Colour.G = 1.0f; //Same as Colour.Y = 1.0f;
Colour.B = 1.0f; //Same as Colour.Z = 1.0f;
Colour.A = 1.0f; //Same as Colour.W = 1.0f;
vect4 Point;
Point.X = 0.123f;
Point.Y = 0.23f;
Point.Z = 0.3f;
Point.W = 1.0f;
//...etcThis allows you to use this vector class with XYZW notation, or RGBA, or UVST, which is similar how you can access vector components in vertex/fragment shaders.
Another use of union notation is mapping larger data types over a collection of smaller types. For example,
union
{
int I;
char C[sizeof(int)];
} Test;
You can access different parts of the integer without masking. Note that there are pitfalls with this, the mapping order of the array will be architecture dependent. In fact, you can use this type of union to test for machine endian-ness. For example:
Test.I = 0x000F;
if (Test.C[0] == 0x0F)
{
//Little endian CPU
}
else
{
//Big endian CPU
}
You can map float data types across integers, use bit mask manipulation on the integer mapping to modify the float data.
union
{
float F;
int32 I;
} Blah;
Blah.F = -1.0f;
Blah.I &= 0x7FFFFFFF; //Clear sign bit (warning: machine dependent operation)
float f = Blah.F; //Should be 1.0f nowI have used unions in a similar manner to convert exotic 80-bit long double types to 64-bit double using bit manipulation.
It's quite rare when you need to do something like this, and its best to avoid if you can.
Isn't using unions in this way undefined behaviour according to the standard? I was told that the standard only supports accessing the element in a union which was last written to, in which case it's only safe to use unions to save space. Combine this with the rules on strict aliasing, and pretty much any form of type pruning relies on compiler specific behaviour.
Are there specific compilers where using unions for type pruning does not work though?
Are there specific compilers where using unions for type pruning does not work though?
I was told that the standard only supports accessing the element in a union which was last written to, in which case it's only safe to use unions to save space. [/quote]
I believe you have point there. Personally I haven't seen any compiler that does not exhibit the behaviour I posted earlier. I dare say, if the bit-field size of each union member is identical, and that their mapping is guaranteed to be at the same physical memory location, then you can expect this behaviour to be consistent. Unless of course there is some kind of data conversion behind the scenes when it's written to memory. I've seen unions used as a crude reinterpret_cast mechanism in C; not exactly 100% safe, but generally they were not a problem if some precautions were taken.
Isn't using unions in this way undefined behaviour according to the standard? I was told that the standard only supports accessing the element in a union which was last written to, in which case it's only safe to use unions to save space. Combine this with the rules on strict aliasing, and pretty much any form of type pruning relies on compiler specific behaviour.
Are there specific compilers where using unions for type pruning does not work though?
Yes that and most of what has been posted here is undefined behaviour according to the standard. You really should have an identifier which signals which element is currently stored in the union, then only read this element and none of the others.
Unions are also pretty useful in pooling-- assuming your pooled objects are big enough, you can temporarily reinterpret them as being, say, a node in a doubly-linked list. This also has the added benefit of being standards-compliant!
and pretty much any form of type pruning relies on compiler specific behaviour.[/quote]
You mean "type punning", for which there are actually two standard and perfectly safe methods - serializing through char*, which has a special dispensation concerning aliasing, or memcpy.
I've seen unions used as a crude reinterpret_cast mechanism in C; not exactly 100% safe, but generally they were not a problem if some precautions were taken.[/quote]
Is that the way you drive, or want an airline to operate - "not exactly 100% safe, but generally [..] not a problem"?
I don't see why an incorrect and illegal solution is preferable to memcpy, which is hardly more complicated.
Yes that and most of what has been posted here is undefined behaviour according to the standard. You really should have an identifier which signals which element is currently stored in the union, then only read this element and none of the others.[/quote]
Good advice.
Incidentally, I often engage in bit-twiddling, and have yet to find a helpful and safe use for unions in that context.
You use unions to save memory. Think of a RPG. An item can be a potion, a weapon, an armor, a quest item, a scroll, a permanent stat up, etc. So an item needs to provide a member for health healing, mana healing, and buffing or debuffing in case it is a potion, attack power, chance to hit, and critical hit chance in case it's a weapon, armor class, magic resistance and evasion in case it's armor, spell to use for scrolls, etc. So that's a lot of variables even though you only ever use two or three at a time. This is a very good place to use a union, where the specific variables are stored into a union of structs, each containing the variables of a particular class of item.
Of course, using unions and data structs is an older style of programming. Polymorphism and real-time type information are a much more powerful way to make similar items with different content. However, in the example above, inventory is something you probably want serialized, and serializing polymorphic objects isn't always simple, whereas a simple byte-by-byte copy operation will do the trick for unions and data structures, so it's not to say there's no advantage to unions versus polymorphism.
Of course, using unions and data structs is an older style of programming. Polymorphism and real-time type information are a much more powerful way to make similar items with different content. However, in the example above, inventory is something you probably want serialized, and serializing polymorphic objects isn't always simple, whereas a simple byte-by-byte copy operation will do the trick for unions and data structures, so it's not to say there's no advantage to unions versus polymorphism.
Others may have shown the most common using of union.
Here is a less common but also very useful trick:
Then you can use a same pointer in both type of TypeA and TypeB, no needing any type cast.
Qt used this trick in some container classed, I also used it in my code.
Here is a less common but also very useful trick:
union WithOrWithoutName {
TypeA * pa;
TypeB * pb;
};
Then you can use a same pointer in both type of TypeA and TypeB, no needing any type cast.
Qt used this trick in some container classed, I also used it in my code.
Please don't write, much less advocate, such non-standard-conforming code.
Both C99 and C++03 say only one union member can be active at a time and provide an explicit exception for accessing a common sequence (usually a type tag) in Bearhugger's union-of-structs. This exception would not be necessary if it were generally legal to access a different field than was last written.
It's important to stamp out such sloppy and senseless non-conforming code because it interferes with aliasing analysis (arguably one of the most important optimizations).
Both C99 and C++03 say only one union member can be active at a time and provide an explicit exception for accessing a common sequence (usually a type tag) in Bearhugger's union-of-structs. This exception would not be necessary if it were generally legal to access a different field than was last written.
It's important to stamp out such sloppy and senseless non-conforming code because it interferes with aliasing analysis (arguably one of the most important optimizations).
A common use is to lay out a friendlier structure over the top of a larger type which facilitates, for example, SIMD operations, a'la:
struct Vec4{
union {
__m128 v;
struct { float x, y, z, w; };
};
};
Can people stop posting this crap as if it is defined, or at least state that it is platform specific. Ravyne has taken this to another level filled with even more undefined/illegal goodness, requiring language extensions.
Maybe this is should be in the FAQ.
Fair enough that I didn't take great pains to enumerate potential downsides, but to be fair I gave that code no endorsement either -- I simply put it forward as one (rather common) example of its use. I didn't even pull that from my own code, but did a quick Google search and pulled it from the first hit, which happened to be a thread here on gamedev. I could have taken the time to vet the code, but I was in a rush.
In any event, if you have an issue with the code itself, lets pick it apart and demonstrate what's wrong with this (again, common -- even if platform specific) practice, rather than leveling near-accusations that I've undertaken this enterprise as some sort of malice or ignorance-driven desire to mislead.
In any event, if you have an issue with the code itself, lets pick it apart and demonstrate what's wrong with this (again, common -- even if platform specific) practice, rather than leveling near-accusations that I've undertaken this enterprise as some sort of malice or ignorance-driven desire to mislead.
A union like that between a struct and a native SIMD type can be hard to avoid in performance-critical code (such as a vector/matrix math library on a console) even though it's platform- and even compiler-specific. On the other hand, you're probably better off writing accessor functions using SIMD intrinsics since you can avoid writing a SIMD register to memory and reading a component back into a floating-point register.
Union are dangerous power tools that should be used only when necessary and when you know what you're getting yourself into. Most of the time, there are cleaner or safer ways to attack the problem.
Union are dangerous power tools that should be used only when necessary and when you know what you're getting yourself into. Most of the time, there are cleaner or safer ways to attack the problem.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement