Sign in to follow this  
karwosts

C++: Should I use a reference member here?

Recommended Posts

I'm working on a project, and in my project I've created a class Vector3. Very simplified, it looks something like this:
class Vector3  
{
public:
        ...(lots of member functions)...
	float i;
	float j;
	float k;
};

Now I've happily used this for a while, but I come to a point where I would like the ability to cast this object to a float*, which would just be a float of three values representing i,j, and k. I realize now that I probably need to have i,j,k stored as a float[3] array internally so I can return a pointer to it. Problem is I already have 500 references to vector.i, etc strewn about my project, and I kind of like having the ability to be able to reference the values that way. My first thought would be to store the data in a private float[3], and then let i,j,k be references to those values:
class Vector3  
{
public:
        ...(lots of member functions)...
	float& i;
	float& j;
	float& k;
private:
	float data[3];
};

But now I'm thinking that this is unnecessarily doubling the size of my class (now holds three pointers and three floats, not sure if references take up memory though). Also now I think I have to define a custom copy and assignment constructor because the default constructor no longer works, and this is starting to feel a little more complicated than it should. I'm wondering too if this will be slower than before if there is some cost with dereferencing the references every time? Basically I'm wondering if there is some construct I should use to tell the program: when I say vector.i, I really want vector.data[0] Is using the references the right way to go? Is there some other method that would be better? Thanks for any input!

Share this post


Link to post
Share on other sites
Thanks to both of you guys. Union was a good idea, I rarely have used them and I wouldn't have thought of it.

For now I'm going ahead with anonymous union / named struct. I like to be able to access elements by name and by array, and it only took me 5 minutes to update all of the references.

However I tried codepad again with this and it still doesn't like it. The code is working fine in MSVC++ (although I know that can be non-standard).

I believe anonymous union is legal in C++, is there something else that is wrong with this (non-standard-complient)?

Codepad

I named my struct elem, but when I reference v.elem.x it errors:

Line 12: error: expected constructor, destructor, or type conversion before '.' token

Share this post


Link to post
Share on other sites
Quote:
Original post by karwosts
I believe anonymous union is legal in C++, is there something else that is wrong with this (non-standard-complient)?
Anonymous unions are allowed; anonymous structs are not. By default, however, all major C++ compilers support them.

Share this post


Link to post
Share on other sites
If it were me, I would just go ahead and return &i for your float*. (And ijk for a vector? Wtf?) Appropriate use of pragma should control the structure layout appropriately, and a BOOST_STATIC_ASSERT can seal the deal.

Share this post


Link to post
Share on other sites
I'm not sure if this would help or not, but my suggestion would be to create a member operator[] (don't forget you need both const and non-const versions), that returns either i,j or k, depending on the subscript. So something like the following:



// Vector3.h

class Vector3
{
public:
// stuff

float & operator[](const int subscript);
const float & operator[](const int subscript) const;

float i;
float j;
float k;
}

// Vector3.cpp

float & Vector3::operator[](const int subscript)
{
switch(subscript)
{
case 0:
return i;
break;
case 1:
return j;
break;
case 2:
return k;
break;
default:
// Handle out-of-bounds error here
}
}

const float & Vector3::operator[](const int subscript) const
{
switch(subscript)
{
case 0:
return i;
break;
case 1:
return j;
break;
case 2:
return k;
break;
default:
// Handle out-of-bounds error here
}
}

int main()
{
Vector3 v;
v.i = 3;
v[0] = 2; // v.i = 2

return 0;
}



I didn't test it or anything, but I don't see why it wouldn't work. It would prevent bloating your class by the size of three more floats, allow your current code to work and look cleaner than v.data[]

Share this post


Link to post
Share on other sites
Quote:

If it were me, I would just go ahead and return &i for your float*. Appropriate use of pragma should control the structure layout appropriately


I wasn't aware that those floats are guaranteed to be contiguous in memory, but rather at the mercy of the compiler. What pragma are you suggesting? That sounds like something I would expect to work 99 times out of 100 but that I couldn't guarantee.

Quote:
(And ijk for a vector? Wtf?)

Isn't this pretty common notation?


Thanks for the suggestion Raislin, but at this time I wasn't really looking for a way to access the elements with array notation. I just need an array of 3 floats I can send to OpenGL API, while I was hoping to maintain compatibility with my older code that uses ijk notation.

Thanks again, learning some new things today :)

Share this post


Link to post
Share on other sites
Sorry but I have to lol, all you want to do is 'cast' your vector class into an array of floats right?

So then why has noone offered this solution:
#include <iostream>

class Vector3
{
public:
float x, y, z;
Vector3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}

operator float *( ); // add this declaration
operator const float *( ) const; // add this declaration
};

Vector3::operator float *( ) // add this definition with 'x' swaped for 'i'
{
return &x;
}

Vector3::operator const float *( ) const // add this definition with 'x' swaped for 'i'
{
return &x;
}

void Print(float arr[], unsigned int size)
{
for (unsigned int i = 0; i < size; ++i)
std::cout << arr[i] << ' ';
std::cout << std::endl;
}

int main(int argc, char** argv)
{
Vector3 Vec(1.4f, 10.51f, 3.3f);
Print(Vec, 3);
return 0;
}







Theres a working example program of how you can construct your Vector class so that you can cast it to a float* which can be indexed like an array and passed to those OpenGL (and Direct3D) functions.

I use this method for my Matrix, Vector, Color and other classes and i believe ive also seen Microsoft implement a casting operator for there D3DXMATRIX class.

PS: No, (i,j,k) is not standard notation for a vector, it is (x, y, z, w) depending on how many dimensions your vector class covers (four is usually the maximum as for as games go, 'w' has something to do with homogenous coordinates and matrices, im not sure i didnt really read into it that much).

[Edited by - CodeCriminal on March 6, 2010 7:18:59 PM]

Share this post


Link to post
Share on other sites
Oh, ok. I misunderstood the problem. Something along the lines of this:

Quote:
Original post by CodeCriminal
all you want to do is 'cast' your vector class into an array of floats right?


is a similar solution and would have been my next suggestion, had CodeCriminal not offered it first.

(x, y, z) is the standard notation for vectors, but in mathematics, the unit vectors are i = <1, 0, 0>, j = <0, 1, 0> and k = <0, 0, 1>, and so vectors are commonly written with i, j, and k looking like axis labels, when it is actually just scalar multiplication and addition at work.

As such, I certainly would not be uncomfortable using i, j and k as labels for the vector components.

Share this post


Link to post
Share on other sites
CodeCriminal, I think that is what Promit was suggesting, although that is again dependent on the guarantee that those members (x,y,z) will always be contiguous in memory, and I wasn't aware that that was true.

Share this post


Link to post
Share on other sites
Quote:
Original post by karwosts
CodeCriminal, I think that is what Promit was suggesting, although that is again dependent on the guarantee that those members (x,y,z) will always be contiguous in memory, and I wasn't aware that that was true.


True, he did suggest it first, but i didnt realise until after i had written my post (my apologies). And your right about (x,y,z) needing to be contiguous in memory for this solution to work.

However, this approach is used in Microsofts very own D3DX library to pass matrices and colors (among other things i may not be aware of) to functions/methods in Direct3D, and since many people use D3DX you would think someone would have spotted a problem by now if they were not contiguous in memory. So I can only assume that they are guaranteed to be, until my assumption is proven to be false.

Quote:
If it were me, I would just go ahead and return &i for your float*. (And ijk for a vector? Wtf?) Appropriate use of pragma should control the structure layout appropriately, and a BOOST_STATIC_ASSERT can seal the deal.


One question though, which pragma directive are you refering to?

[Edited by - CodeCriminal on March 6, 2010 8:41:10 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by CodeCriminal
So I can only assume that they are guaranteed to be, until my assumption is proven to be false.

It's not. See section 9.2 paragraph 12 in the C++ Standard for the relevant verbage. However, it would take a seriously messed up implementation for the variables not to be contiguous. If the lack of guarantee bothers you, put a static assert on it and worry about it if it ever trips.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Quote:
Original post by CodeCriminal
So I can only assume that they are guaranteed to be, until my assumption is proven to be false.

It's not. See section 9.2 paragraph 12 in the C++ Standard for the relevant verbage. However, it would take a seriously messed up implementation for the variables not to be contiguous. If the lack of guarantee bothers you, put a static assert on it and worry about it if it ever trips.


Ah, thanks for the heads-up SiCrane, though I've never had any problems with it before I'll keep that in mind.

Share this post


Link to post
Share on other sites
Quote:
Original post by karwosts
Quote:
(And ijk for a vector? Wtf?)

Isn't this pretty common notation?


For mathematicians, sure. For computer programmers, not so much. :) xyz is much more common.

Share this post


Link to post
Share on other sites
Since the OP has been answered...
Quote:

For mathematicians, sure. For computer programmers, not so much. :) xyz is much more common.


I have reservations about this statement because it seems to suggest that computer programmers are either game/graphics people or language students instead of real people solving real problems. A mathematician writing a math program IS a computer programmer. Excuse me if you meant it as a joke ( and the derail but... )

I believe Bjarne Stroustrup has bemoaned this notion:
http://itmanagement.earthweb.com/features/article.php/12297_3789981_3/Bjarne-Stroustrup-on-Educating-Software-Developers.htm

Quote:

Actually, I think the ultimate aim is to make programming more of an engineering discipline, more mathematical or scientific; “craft” and “art” are both needed, but there ought to be a scientifically based core on which people can base their craft and art. Software design and implementation is more than a craft; there is more math, science and engineering to know and apply than is customary for fields we call “crafts.” Incidentally, I find it appalling that you can become a programmer with less training than it takes to become a plumber.


http://www.artima.com/cppsource/cpp0x.html
Quote:

...Many C++ users quite reasonably don't want to become C++ experts—they are experts in their own fields (e.g., physicists, graphics specialists, or hardware engineers) who use C++. In my opinion, C++ has become too "expert friendly"...

Share this post


Link to post
Share on other sites
That's true, and why I feel my explanation in a post further above is more thorough. However, I don't think that differing notation in mathematics being different than notation in C++ should imply such a thing. Notation differs between programming languages and APIs (think left-handed vs right-handed coordinate systems).

In this case, however, the standard notation in both math (i.e. Calculus) and C++ is (x, y, z) for vectors, or <x, y, z> in some cases of math. The confusion comes my above explanation (for example, x = m*i where m is a scalar value and i is the unit vector in the positive direction of the x-axis).

Share this post


Link to post
Share on other sites
It seems like a lot of mathematical literature names vector coordinates of a vector v as (v1, v2...vn) so perhaps the operator[] is the closest best conventional representation that generalizes access for inter-disciplinary uses?

I wish the C++ standard library had just named "std::vector" "std::array" instead.

Share this post


Link to post
Share on other sites
Quote:
Original post by m3mb3rsh1p
It seems like a lot of mathematical literature names vector coordinates of a vector v as (v1, v2...vn) ...

Similar to me: I'm used to index the component kind, e.g. { s0, s1, ... } for the (scalar) components of vector, { v0, v1, ... } for the (vector) components of matrices, ... and so suppressing any notional dependence on whether the structure works in RGB color space, XYZ spatial space, UV texture space, or whatever. Its just my personal preference.

Share this post


Link to post
Share on other sites
Quote:
Original post by CodeCriminal
One question though, which pragma directive are you refering to?
I assume it's
#pragma pack(1)
class Vector3
{
public:
...(lots of member functions)...
float i;
float j;
float k;
};
#pragma pack()
or the push/pop variants. Unfortunately, #pragmas are compiler dependent. The above is known from M$ compilers.

The way GCC suggests is different; something like
class Vector3  
{
public:
...(lots of member functions)...
float i;
float j;
float k;
} __attribute__((packed));
for packing the entire structure. However, at least older versions of GCC supported the #pragma way for compatibility reasons as well.

Share this post


Link to post
Share on other sites
Quote:
Original post by karwosts
Quote:

If it were me, I would just go ahead and return &i for your float*. Appropriate use of pragma should control the structure layout appropriately


I wasn't aware that those floats are guaranteed to be contiguous in memory


If they weren't then the union/struct solution wouldn't work either, given that it also relies on the floats being contiguous.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this