Reputation: 580
I have a class vector
class vector3f
{
public:
float x;
float y;
float z;
};
If I want to access one of the members I have to type vec.x, vec.y and vec.z. Now, I have an algorithm that should access the data members per index, for example:
for(int i = 0; i < 3; i++)
{
vec[i] = i*i;
}
Doing this without an index would result in several if-conditions:
for(int i = 0; i < 3; i++)
{
if(i == 0){vec.x = i*i;}
else if(i == 1){vec.y = i*i;}
else if(i == 2){vec.z = i*i;}
}
Is there a way to index the data members so that they are resolved in compile-time? I don't want to use an array in the vector class because accessing the data members with vec.x, vec.y and vec.z is more convenient for the programmer. What would be the best solution to avoid these if-conditions?
Upvotes: 6
Views: 4739
Reputation: 18461
Not absolutely at compile time, but you can optimize.
You can use pointer-to-member-variable, and then its array. Start with:
float vector3f::*px;
px = &vector3f::x;
(vec.*px) = 10.2;
You then declare array of pointer-to-member:
float vector3f::*pArray[3];
Assign them:
pArray[0] = &vector3f::x;
pArray[1] = &vector3f::y;
pArray[2] = &vector3f::z;
Use them:
(vec.*pArray[0]) = 10.2; // This is vector3f::x
(vec.*pArray[1]) = 1042.5; // This is vector3f::y
And appropriately change the loop body to index appropriate pointer-to-member!
Upvotes: 1
Reputation: 33
An interesting alternative can be had by using references.
class vector3f
{
public:
float &x, &y, &z;
float value[3];
vector3f() :
x(value[0]), y(value[1]), z(value[2]){}
};
Here we bind x to value[0], y to value[1] and z to value[2] - they are aliases for entries in the array, but note that they do allocate an extra 4 bytes per reference in the class. This way, we can name our array for the user access convenience.
We also can avoid a runtime call to an overloaded operator [
(but a good compiler should inline that access anyway on a switch/case).
Example usage:
vector3f test;
test.value[0] = 227.f;
test.value[1] = 42.f;
test.value[2] = -1.f;
printf("Values are %lf, %lf and %lf.",test.x,test.y,test.z);
Resultant output:
Values are 227.000000, 42.000000 and -1.000000.
Upvotes: 0
Reputation: 171197
You can overload operator[]
for your class to localise those if
conditions (or preferably a switch
) into one place:
class vector3f
{
public:
float x;
float y;
float z;
float& operator[] (size_t i)
{
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
default: throw "something";
}
}
float operator[] (size_t i) const
{ return (*const_cast<vector3f*>(this))[i];
}
That's the generic and safe way of implementing this.
If your class is actually a standard-layout class1, as you have shown, and if you know the alignment and padding scheme of your compiler/platform, and if you know from these that the float
s will just be arranged after each other in memory, you can implement the operator like this as well:
float& operator[] (size_t i)
{
return (&x)[i];
}
This treats x
as the start of an array of float
s. It will be faster, but relies on the ifs I mentioned above, and does not do bounds checking.
1 Which basically means it doesn't have non-empty base classes, doesn't have virtual
functions and doesn't have a data member of non-standard-layout type.
Upvotes: 8