View more

View more

View more

### Image of the Day Submit

IOTD | Top Screenshots

### The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

# Is this a valid usage for unions?

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

16 replies to this topic

Posted 09 May 2012 - 12:25 PM

Hello all,

recently I decided to discard my hand-written math library code and use the GLM library.

Because I want to make it "feel" like my own code need some thin wrapper around it. Because performance matters I don't want to
write the wrapper using inheritance like this:

class Vector3 : public glm::dvec3
{
};


Now I have the idea to use a union and write code like this:

union Vector3
{
public:
Vector3(double x, double y, double z)
: v(x, y, z)
{
}

double x, y, z;

private:
glm::dvec3 v;
};


Is this valid code (using C++11)?

### #2e‍dd  Members

Posted 09 May 2012 - 01:03 PM

Access to members of a virtual base and calling virtual functions will typically result in a small performance penalty, but in the kind of inheritance you've shown, there will be none.

The doubles x, y and z in your union all occupy the same memory location. Presumably that's not intended.

I also suspect that you're heading down the road of type punning through the union, which strictly speaking is illegal, though some compilers explicitly support it. Take care.

My recommendation: typedef glm::dvec3 Vector3;

Posted 09 May 2012 - 01:04 PM

of course
double x, y, z

does not work.

So I have to use something like this:
union Vector3
{
public:
Vector3(double x, double y, double z)
: v(x, y, z)
{
}
inline double x() { return d[0]; }
inline double y() { return d[1]; }
inline double z() { return d[2]; }

private:
double d[3];
glm::dvec3 v;
};


Actually I wanted to avoid the method calls for accessing the data members...

Are compilers smart enough to optimize this code (this post) so that it performs as fast as the first one (previous post)?

Posted 09 May 2012 - 01:06 PM

Using
typedef glm::dvec3 Vector3

was my first idea BUT I want things like the cross product to be used as member functions. GLM implements them as
normal functions taking the two vectors as parameters.

But when the inheritance I have suggested does not have any performance penalties I will go that way.

### #5e‍dd  Members

Posted 09 May 2012 - 01:12 PM

GLM implements them as normal functions taking the two vectors as parameters.

That makes perfect sense I'd say; as a cross product is a pure function, to have it appear to "act upon" any particular vector would be strange. A cross product also has no need to access or manipulate the (non-existent) private state of a vector. Free functions also play nicer with generic code, FWIW.

Posted 09 May 2012 - 01:14 PM

Ok for the cross product it makes sense. But normalize?

### #7e‍dd  Members

Posted 09 May 2012 - 01:16 PM

Same reasoning (IMO). One input, one output, no dependency on the private state.

Edited by edd², 09 May 2012 - 01:17 PM.

Posted 09 May 2012 - 01:17 PM

I don't think you want to be using a union here, it might work one way or another, but I can't see this as being considered good practice

I'd just go with the inheritance if you want to add some of your own functions, I can assure you that the "performance penalty" that could possibly be introduced by it will be least of your worries in the end. Otherwise go for the typedef, clean and simple.

By the way, only worry about optimizing these little things like inheritance overhead if they actually pose a problem, and in this case it'll probably never become a problem. In the end there will be loads of better candidates for optimization before you should even consider looking at overhead caused by language mechanisms.

I gets all your texture budgets!

Posted 09 May 2012 - 01:22 PM

Same reasoning (IMO). One input, one output, no dependency on the private state.

I think you are right, at least if normalize creates a new vector. But when I really want to normalize the instance normalize is invoked on, it needs access to the state. But maybe that is not the problem as the state is public...

I don't think you want to be using a union here, it might work one way or another, but I can't see this as being considered good practice

"Good practice" is enough of a reason for me to not use unions for this

So decision has to be made between "inheritance" and "typedef + free functions"

Posted 09 May 2012 - 01:28 PM

You might want to look at the coding style for the rest of your code (I assume you're writing a game?) and adapt to what fits best with that so you can save yourself the headache of having to keep different coding styles in mind later on

If there's no other code to speak of and if you're comfortable with it I'd say go with edd's suggestion.

Edited by Radikalizm, 09 May 2012 - 01:29 PM.

I gets all your texture budgets!

Posted 09 May 2012 - 01:34 PM

Member functions would fit better to the rest of my code. But for the free functions I would rather like to use something like Vector3::normalize(v). So I would need to
use inheritance + static methods?! The typedefs would also be fine but Is it possible to typedef the free functions of glm (never done something like that)?

I think of:
namespace Math
{
typedef glm::dvec3 Vector3;
typedef glm::normalize Normalize;
};


Maybe I could just write inline functions for my desired "renaming":

namespace Math
{
inline Vector3 Normalize(const Vector3& v)
{
return glm::normalize(v);
}
}


Posted 09 May 2012 - 01:45 PM

The typedef keyword is for use with datatypes only

Inlining would work, but IMO you're going to end up doing lots of work wrapping all the functions provided by glm without any real purpose

I gets all your texture budgets!

Posted 09 May 2012 - 01:51 PM

Inlining would work, but IMO you're going to end up doing lots of work wrapping all the functions provided by glm without any real purpose

The purpose for me is that all my code looks consistent and that I could change the math library later on. And fortunately is is not that much work because I would just wrap the functions I actually need.

Thanks to both of you for the help

### #14Ravyne  Members

Posted 09 May 2012 - 02:05 PM

Good OOP practice prefers to minimize interfaces that have access to internal state (including 'friend' functions or 'friend' classes), and prefers non-member, non-friend functions located in the same namespace--because of Koenig Lookup, these functions are still effectively a part of the class interface, but better promote encapsulation.

True member functions should be used to provide the minimum necessary interface needed to implement higher-level interface functions or when the object is itself modified by the operation. Using these member functions should be the preferred means of implementing higher-level functionality through non-member, non-friend functions, but non-member friend functions can be used in cases when a function requires non-modifying access to object internals (though this usually indicates that your "true member" interface is lacking) using a const reference to the object.

Edited by Ravyne, 09 May 2012 - 02:08 PM.

throw table_exception("(ノ ゜Д゜)ノ ︵ ┻━┻");

Posted 09 May 2012 - 02:14 PM

Really good explanation!!!

So best choice for e.g vector normalization would be a free function in the Math namespace which takes a const reference to a vector and to have a member function
which does not take parameters but normalizes the vector instance itself?

### #16clb  Members

Posted 09 May 2012 - 02:36 PM

Actually I wanted to avoid the method calls for accessing the data members...

Are compilers smart enough to optimize this code (this post) so that it performs as fast as the first one (previous post)?

Using

typedef glm::dvec3 Vector3

was my first idea BUT I want things like the cross product to be used as member functions. GLM implements them as
normal functions taking the two vectors as parameters.

But when the inheritance I have suggested does not have any performance penalties I will go that way.

Yes, if you inherit a value type, and always use the object via the derived class and retain the POD-likeness of the object (no custom ctors/dtors/assignments or copy-ctors), you will not have to use a virtual dtor, and there will be no performance penalties.

If you are interested in having a look at another library which does give you accesses to vector .x/.y/.z without function calls, and has .Cross() as a member function (but also as a freestanding function), see my MathGeoLib library. The float3 class reference is here.
Me+PC=clb.demon.fi | C++ Math and Geometry library: MathGeoLib, test it live! | C++ Game Networking: kNet | 2D Bin Packing: RectangleBinPack | Use gcc/clang/emcc from VS: vs-tool | Resume+Portfolio | gfxapi, test it live!

### #17Ravyne  Members

Posted 09 May 2012 - 02:47 PM

I'd do it something like this (syntax may not be exact since I don't have a compiler in front of me):

namespace math {
class vector {
...
public:
vector& normalized() {
float inverse_length = 1 / this.length();

this.x *= inverse_length;
this.y *= inverse_length;
this.z *= inverse_length;

return *this;
}
...
};

vector& normalized_copy(vector to_normalize) {  // notice I take this by value
}
}


Here, the non-member, non-friend function takes the vector by value and then uses the member function normalize() to do the work. This pattern of passing in by value, modifying that object, and returning it modified and by reference enables the Return Value Optimization (RVO), so it not only re-uses the member implementation, but is efficient as well. RVO basically eliminates redundant copies of the object. Using a similar pattern, you can implement binary +, -, *, / and other operators as non-member, non-friend functions by re-using the unary member operators for +=, -=, *=, /=, etc.

Edited by Ravyne, 09 May 2012 - 02:49 PM.

throw table_exception("(ノ ゜Д゜)ノ ︵ ┻━┻");

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.