-2

I've got this code that I just compiled successfully :

template <typename T, unsigned int N>
struct Vector
{
    struct Vec1
    {
        T x;
    };

    struct Vec2 : public Vec1
    {
        T y;
    };

    struct Vec3 : public Vec2
    {
        T z;
    };

    struct Vec4 : public Vec3
    {
        T w;
    };

    template <unsigned int N>
    union Data
    {
        std::array<T, N> components;
    };

    template <>
    union Data<1>
    {
        Vec1 vec;
        std::array<T, 1> components;
    };

    template <>
    union Data<2>
    {
        Vec2 vec;
        std::array<T, 2> components;
    };

    template <>
    union Data<3>
    {
        Vec3 vec;
        std::array<T, 3> components;
    };

    template <>
    union Data<4>
    {
        Vec4 vec;
        std::array<T, 4> components;
    };

    Data<N> data;
};

It works as intended, however I would like the struct Vector to expose the data's variables as its own member variables.

Is it possible?

The solution would allow me to do Vector<int, 3> vec; vec.x ...; vec.components[0] ...;

The purpose of the union is to access easily both the vector's components as an array and individually.

Also, if you happen to know a better way to implement the templated union Data specializations, please say so as I find it kinda hard coded. It would be perfect to recursively add variables without having to add the variables of the previous specialization.

For example, I would only need to declare T x once.

Benoît Dubreuil
  • 650
  • 1
  • 11
  • 27
  • 2
    How are you planning to use that `union`? You are aware that accessing any `union` member that wasn't used for initialization is _undefined behavior_, no? – πάντα ῥεῖ Nov 19 '18 at 17:43
  • 2
    What is the purpose of the union? If you are using it to access the individual elements of the array you are in for a [surprise](http://coliru.stacked-crooked.com/a/fd7619def2861827) – NathanOliver Nov 19 '18 at 17:44
  • The purpose of the union is to access easily both the vector's components as an array or individually. @πάνταῥεῖ Thanks! I didn't know. – Benoît Dubreuil Nov 19 '18 at 17:45
  • 1
    @Thecheeselover The union won't give that to you. You'll have to specialize the entire class and use reference members to the array. Or you could provide functions that have the variable name, and use those as accessors. – NathanOliver Nov 19 '18 at 17:47
  • 1
    *compiled successfully* - [this code does not compile successfully](https://wandbox.org/permlink/qBWozDUm96eaDtmi) also to access both vector components and array you will need to pack components into struct or only one of them can be active. Basically all you want to do is to add getters like `get_x` `get_y` into `std::array` – user7860670 Nov 19 '18 at 17:47
  • @πάντα ῥεῖ It's technically UB but also widely used. It's the basis of vector classes in the GLM library for instance. – Fibbs Nov 19 '18 at 17:51
  • @VTT Well it compiles in VS. I know it's not perfect, as it does not initializes the arrays. I'm just trying to figure out how to expose the union variables. @NathanOliver Hmmm I feared that I would have to specialize the whole class :/ Is it possible to partially specialize a class? For example, I wouldn't have to redeclare the array and functions, only the `x, y, z...`. – Benoît Dubreuil Nov 19 '18 at 17:51
  • 1
    @Fibbles The `std::array` won't map properly to the other `union` members anyways. – πάντα ῥεῖ Nov 19 '18 at 17:52
  • You can if you use inheritance. Make the member part the base class and the functional part the derived part. – NathanOliver Nov 19 '18 at 17:53
  • @πάνταῥεῖ Are you saying the union won't actually work except, except for the fact that it's not initialized? – Benoît Dubreuil Nov 19 '18 at 17:53
  • Well, I'll have to check how much memory and performance inheritance costs. I'm aiming to make it as fast as GLM, well at least it's my goal. – Benoît Dubreuil Nov 19 '18 at 17:55
  • 1
    Regular inheritance is a compile time thing. It doesn't have a run time cost. – NathanOliver Nov 19 '18 at 17:55
  • ­@NathanOliver Oh wow, I didn't know that. Thank you! I'll try something less complicated then with inheritance. – Benoît Dubreuil Nov 19 '18 at 17:56
  • 1
    @πάντα ῥεῖ Yes, he just needs to change it to a union containing an array and a struct of components. (And some asserts to check for unexpected padding) – Fibbs Nov 19 '18 at 17:57
  • Thanks @πάνταῥεῖ! Do you recommend a tutorial or article for unexpected padding? – Benoît Dubreuil Nov 19 '18 at 18:57
  • @Thecheeselover _"Do you recommend a tutorial or article for unexpected padding?"_ No. That would be _off-topic_ and I won't do that, not even in comments most probably. Just that much: Drop using a `union` for such cases, write a _mapper_ as proposed in the answer here instead. – πάντα ῥεῖ Nov 19 '18 at 19:01

1 Answers1

5

I think you need to bring some clarity to your design and the code.

Use of

template <>
union Data<3>
{
    T x;
    T y;
    T z;
    std::array<T, 3> components;
};

does not sound right. You need to have {x, y, z} or components, not x, or y, or z, or components. What you need is something along the lines of

template <>
union Data<3>
{
    struct
    {
       T x;
       T y;
       T z;
    } members;
    std::array<T, 3> components;
};

Having said that, the cleanest member variable is just

    std::array<T, N> components;

As far as the member variables are concerned, Vector can be defined as:

template <typename T, unsigned int N>
struct Vector
{
   std::array<T, N> components;
};

If you need to expose the elements of components through x, y, and z-like abstractions, it will be better to add member functions.

template <typename T, unsigned int N>
struct Vector
{
   std::array<T, N> components;

   T& x()
   {
      static_assert(N > 0);
      return components[0];
   }

   T& y()
   {
      static_assert(N > 1);
      return components[1];
   }

   T& z()
   {
      static_assert(N > 2);
      return components[2];
   }
};

with the above definition of Vector, the following main function should work.

int main()
{
   Vector<int, 1> v1;
   v1.x() = 20;

   Vector<int, 2> v2;
   v2.x() = 20;
   v2.y() = 30;

   Vector<int, 3> v3;
   v3.x() = 20;
   v3.y() = 30;
   v3.z() = 40;
}

If you use

   Vector<int, 2> v2;
   v2.z() = 20;

you should get a compile-time error.

You can add the const versions of the above functions to make the member functions work with const objects too.

template <typename T, unsigned int N>
struct Vector
{
   std::array<T, N> components;

   T& x()
   {
      static_assert(N > 0);
      return components[0];
   }

   T const& x() const
   {
      static_assert(N > 0);
      return components[0];
   }

   T& y()
   {
      static_assert(N > 1);
      return components[1];
   }

   T const& y() const
   {
      static_assert(N > 1);
      return components[1];
   }

   T& z()
   {
      static_assert(N > 2);
      return components[2];
   }

   T const& z() const
   {
      static_assert(N > 2);
      return components[2];
   }
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270