6

Is it possible in C++ to refer to the same variable using different names without using the preprocessor?

To achieve the same effect as this pseudocode

struct vec3f {
    float[3] values;
};

struct color : public vec3f {
    #define r values[0]
    #define g values[1]
    #define b values[2]
};

color c;
c.r = 0.5f;

The following has the right semantics except it allocates space in the struct for the 3 references:

struct color : public vec3f {
    float& r;
    float& g;
    float& b;
    color() : r(values[0]), g(values[1]), b(values[2]) { }
};

Is there a way to get this compile-time name substitution without increasing the size of the struct?

wjd
  • 61
  • 1
  • 3

4 Answers4

10

How about this?

struct vec3f {
    float[3] values;
};

struct color : public vec3f
{
    float& r() { return values[0]; }
    float& g() { return values[1]; }
    float& b() { return values[2]; }
    const float& r() const { return values[0]; }
    const float& g() const { return values[1]; }
    const float& b() const { return values[2]; }
};
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I was trying to avoid the brackets but this is definitely the right way to do it. – wjd Dec 30 '11 at 03:12
2

I am not sure that you want to use inheritance in this case. You might be better of with a plain old union type:

typedef float vec3f[3];
union color {
   vec3f values;
   struct {
      float r;
      float g;
      float b;
   };
};

color c;
c.values[0] = 10;
assert( c.r == 10 );
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
1

ALTERNATIVE 1

You always create a temporary when you want a variable alias. With a good optimizer you will hardly see any performance difference.

struct vec3f
{
    float values[3];
};

struct tempvec
{
    float &r;
    float &g;
    float &b;

    tempvec( vec3f& bar )
        :r(bar.values[0]) 
        , g(bar.values[1]) 
        , b(bar.values[2]){}
};

int main() 
{
    vec3f temp;
    temp.values[0] = 2.40f;

    //when you want to alias values[0] as r do this
    tempvec(temp).r = 42;
    tempvec(temp).g = 42;

    return 0;    
}

ALTERNATIVE 2

If you can verify that memory layout of vec3f and vec3c is the same on your platform and OS.. taking into account padding/alignment etc... you can do

struct vec3f
{
    float values[3];
};

struct vec3c
{
    float r,g,b;
};

int main() 
{
    vec3f temp;
    temp.values[0] = 2.40f;

    vec3c* alias  = reinterpret_cast<vec3c*>(&temp);

    alias->r = 4.2f;
    alias->g = 4.2f;
    alias->b = 4.2f;

    return 0;    
}
parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85
  • Your alternative 2 made me discover the cast operator, so `color` could be written as `struct color { float r, g, b; operator vec3f&() { return *((vec3f*)this); };`. – wjd Dec 30 '11 at 02:39
1

As it happens, I first saw a really neat trick for this several years ago.

The idea is that you give the class named variables in order, and then also have a static const member of array-of-pointer-to-member type. The operator[] is overloaded to look up the appropriate pointer-to-member, use it to select the member from this, and return a reference.

This works because pointer-to-members are not ordinary pointers; they're a little more magical than that. (This is what enables you to create un-bound pointers to member functions, and why they can't be used where plain function pointers are expected).

It also means that you don't have to use any casting tricks, rely on any kinds of alignment, non-portable anonymous-union behaviour, or memory layout guarantees, and you still get to refer to the components of the structure as named fields instead of via accessor functions.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • While a neat trick, I don't see how this helps when `color` should be a `vec3f`. – Mooing Duck Dec 30 '11 at 00:30
  • @MooingDuck If we're going to do something as strange as treating colours as vectors, I hardly see how it matters what direction the inheritance goes in, or even that there is inheritance... so you'd make a `color` with the overloaded `operator[]` and `r/g/b` members, and then typedef `vec3f` to it. – Karl Knechtel Dec 30 '11 at 06:47