wjd
wjd

Reputation: 61

Can you make a struct containing an array member, where each array element also has a name?

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 values[3];
};

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?

Upvotes: 6

Views: 6609

Answers (4)

parapura rajkumar
parapura rajkumar

Reputation: 24403

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;    
}

Upvotes: 1

Karl Knechtel
Karl Knechtel

Reputation: 61509

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.

Upvotes: 1

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 );

Upvotes: 1

Ben Voigt
Ben Voigt

Reputation: 283634

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]; }
};

Upvotes: 10

Related Questions