Search code examples
c++gccstructforward-declarationunions

Named Structures in C++ Unions


In C++, I'm trying to create a specialized point class as a union, like so:

union point
{
  struct { float x, y, z; };
  float val[3];
  float operator[](unsigned i) { return val[i]; }
};

So that I can access the point as an array or as multiple points, for readability.

However, let's say that I want to generalise this a bit:

template<unsigned n>
  union point
  {
    struct { float ???; };
    float val[n];
    float operator[](unsigned i) { return val[i]; }
  };

What can I put for ???? I could have x, x, y, x, y, z, or x, y, z, w depending on what n is. Solution? Forward declarations!

template<unsigned n>
  union point
  {
    struct coords;
    float val[n];
    float operator[](unsigned i) { return val[i]; }
  };

template<>
  struct point::coords<3>
  {
    float x, y, z;
  };

// ...

But this doesn't appear to work. Under the GCC 4.6, it compiles, however, whenever that I try to use the members, like so:

point<3> val;
val.x;

I get the error:

error: ‘union point<3>’ has no member named ‘x’

Even if I change val.x to val.coords::x, I still get the error:

error: ‘union point<3>::coords’ is not a base of ‘union point<3>’

Adding using coords; in the union definition didn't help, either.

Is there any way to accomplish this under the GCC 4.6? Is there a different method of doing this? Is it even possible?


Solution

  • I would suggest using variadic macro to define your union<N> templates.

    template<unsigned int N>
    union point; // declared and undefined
    
    #define DECLARE_POINT(NUM, ...) \
    template<> \
    union point<NUM> \
    { \
      struct { float __VA_ARGS__; }; \
      float val[NUM]; \
    }
    
    #undef DECLARE_POINT
    

    Having done this, you can simply declare/define your various combinations for coordinates (before #undef in this case):

    DECLARE_POINT(1, x);
    DECLARE_POINT(2, x, y);
    DECLARE_POINT(3, x, y, z);
    

    that is equivalent to,

    template<> union point<1> { struct { float x; }; float val[1]; };
    template<> union point<2> { struct { float x, y; }; float val[2]; };
    template<> union point<3> { struct { float x, y, z; }; float val[3]; };
    

    It can be used in the same way you asked:

    point<3> p;
    p.z = 0;
    

    Also, you can put a cross check using some template trickery (static_assert) to check the number arguments(e.g. 1,2,3,...) match the total argument passed (e.g. x,y,z,...).