user3067395
user3067395

Reputation: 580

C++ Index class members

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

Answers (3)

Ajay
Ajay

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

Pcube
Pcube

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

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 floats 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 floats. 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

Related Questions