I have a (column) Vector class which contains an array of values that can be accessed like:
Vec<int, 4> v();
v[0] = -2; // <- set first value to -2
v[1] = 1; // <- set second value to 1
....
But here is my question: How do i create an alias for v[0], v[1], v[2], v[3]?. I would like to define the first 4 values as v.x, v.y, v.z, v.w:
Vec<int, 4> v();
v.x = -2; // <- set first value to -2
v.y = 1; // <- set second value to 1
v.z = 4; // <- set third value to 4
v.w = 2; // <- set fourth value to 2
I should be able to assign and read the values, and I don't want them to be looking like a function, so accessing the first value like:
Vec<int, 4> v();
v.x() = -2; // <- set first value to -2
Is no good. On top of that the vector class is templated and x should only be defined for dimensions >= 1, and y only for dimenions >= 2 ... and so on... How do I achieve this?
Edit: The Vector class has nothing to do with std::vector, it is a mathematical vector resembling an array, in that it is of fixed size and its only used for mathematical operations. (renaming Vector to Vec).
Matrix class:
template <typename T, size_t ROWS, size_t COLS>
class Matrix {
    public:
        T& operator[] (size_t idx) {return m_matrix[idx];}
        T operator[] (size_t idx) const {return m_matrix[idx];}
    private:
        m_matrix[ROWS * COLS]
};
Vector class:
template <typename T, size_t N>
class Vec: public Matrix<T, 1, N>{
    public:
        T& x() {return (*this)[0];}
        T x() const {return (*this)[0];}
        T& y() {return (*this)[1];}
        T y() const {return (*this)[1];}
        T& z() {return (*this)[2];}
        T z() const {return (*this)[2];}
        T& w() {return (*this)[3];}
        T w() const {return (*this)[3];}
};
This works and I am easily able to use enable_if to remove the functions if it is not defined for this dimension, this however isn't syntactically pleasing. Ive tried using references:
template <typename T, size_t N>
class Vec: public Matrix<T, N, 1>{
    public:
        T& x = (*this)[0];
        T& y = (*this)[1];
        T& z = (*this)[2];
        T& w = (*this)[3];
};
But this doesn't work, it doesn't give me an error, but it also does not set the values correctly, when I access them after setting they are undefined.
Edit nr 2: there might just exist an even simpler solution, when my last attempt with references is compiled using Visual Studio community 2015's default compiler, then it works. But when I compile it in Code::Blocks using the GNU GCC compiler, then it doesn't. What does the standard say? Is my solution using references allowed, which compiler is wrong?
This:
template <typename T, int D> struct Vec;
// You have to manually specialize for all needed sizes
template <typename T> struct Vec<T, 4>
{
    T x, y, z, w;
    T &operator[](int index)
    {
        switch (index)
        {
            default: // throw or something?
            case 0: return x;
            case 1: return y;
            case 2: return z;
            case 3: return w;
        }
    }
    const T &operator[](int index) const
    {
        switch (index)
        {
            default: // throw or something?
            case 0: return x;
            case 1: return y;
            case 2: return z;
            case 3: return w;
        }
    }
};
The switching on index is not optimal, but at least it's well-defined.
For matrices I prefer to use Vec<Vec<T, Height>, Width>, which makes mat[x][y] notation work. (Swap x and y if you want to.)
If you accept a C++14 solution, I propose the creation of a template indexed wrapper for x, y, z and w, referenced to T variables
template <typename T, std::size_t>
struct wrapper
 { wrapper (T const &) {} };
template <typename T>
struct wrapper<T, 0U>
 { T & x; };
template <typename T>
struct wrapper<T, 1U>
 { T & y; };
template <typename T>
struct wrapper<T, 2U>
 { T & z; };
template <typename T>
struct wrapper<T, 3U>
 { T & w; };
Next an std::array wrapper that has to be inherited before the indexed wrappers
template <typename T, std::size_t N>
struct arrayWrp
 { std::array<T, N> arr {}; };
Now you can define an helper struct VecH as follows
template <typename T, std::size_t ... Is>
struct VecH<T, std::index_sequence<Is...>>
   : public arrayWrp<T, sizeof...(Is)>, public wrapper<T, Is>...
 {
   using arrayWrp<T, sizeof...(Is)>::arr;
   VecH () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }...
    { }
   T & operator[] (std::size_t i)
    { return arr[i]; }
   T const & operator[] (std::size_t i) const
    { return arr[i]; }
 };
that inherit from arrayWrp and from all wrapper<T, Is> needed and that link references x, y, z and w to arr[0], arr[1], arr[2] and arr[3] rispectively
So Vec become
template <typename T, std::size_t N>
struct Vec : public VecH<T, std::make_index_sequence<N>>
 { };
The following is a full working example
#include <array>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t>
struct wrapper
 { wrapper (T const &) {} };
template <typename T>
struct wrapper<T, 0U>
 { T & x; };
template <typename T>
struct wrapper<T, 1U>
 { T & y; };
template <typename T>
struct wrapper<T, 2U>
 { T & z; };
template <typename T>
struct wrapper<T, 3U>
 { T & w; };
template <typename T, std::size_t N>
struct arrayWrp
 { std::array<T, N> arr {}; };
template <typename, typename>
struct VecH;
template <typename T, std::size_t ... Is>
struct VecH<T, std::index_sequence<Is...>>
   : public arrayWrp<T, sizeof...(Is)>, public wrapper<T, Is>...
 {
   using arrayWrp<T, sizeof...(Is)>::arr;
   VecH () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }...
    { }
   T & operator[] (std::size_t i)
    { return arr[i]; }
   T const & operator[] (std::size_t i) const
    { return arr[i]; }
 };
template <typename T, std::size_t N>
struct Vec : public VecH<T, std::make_index_sequence<N>>
 { };
int main ()
 { 
   Vec<int, 4U>  v4;
   v4.x = 1;
   v4.y = 2;
   v4.z = 3;
   v4.w = 4;
   std::cout << "v4: ";
   for ( auto ui = 0U ; ui < 4U ; ++ui )
      std::cout << ' ' << v4[ui];
   std::cout << std::endl;
   Vec<int, 5U>  v5;  // also over 4
   Vec<int, 3U>  v3;
   v3.x = 10;
   v3.y = 20;
   v3.z = 30;
   // v3.w = 40;  // compilation error
 }
If you don't like the use of the VecH helper struct, you can use partial specialization and a template parameter defaulted to std::make_index_sequence<N> as follows
template <typename, std::size_t N, typename = std::make_index_sequence<N>>
struct Vec;
template <typename T, std::size_t N, std::size_t ... Is>
struct Vec<T, N, std::index_sequence<Is...>>
   : public arrayWrp<T, N>, public wrapper<T, Is>...
 {
   using arrayWrp<T, sizeof...(Is)>::arr;
   Vec () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }...
    { }
   T & operator[] (std::size_t i)
    { return arr[i]; }
   T const & operator[] (std::size_t i) const
    { return arr[i]; }
 };
but I don't know if it's a good idea: someone could try to use Vec as follows
Vec<int, 3U, std::index_sequence<0, 2, 5>>  v;
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With