Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing temporary struct as template argument

I'm in the process of creating a vector class and am trying to figure out ways to reuse the maximum amount of code for different size vectors. Here's a basic example:

template<typename T, unsigned int D>
class Vector
{
public:
    union {
        T v[D];
        struct {
            /* T x;
             * T y;
             * T z;
             * T w;
             */
        };
    };

    Vector()
    {
        for(unsigned int i=0; i<D; ++i)
            (*this)[i] = T(0);
    }
    Vector(T scalar)
    {
        for(unsigned int i=0; i<D; ++i)
            (*this)[i] = scalar;
    }

    inline T operator[](int i) { return (*this).v[i]; }
};

I want the member variables to be publicly accessible. Ex:

Vector<float,2> vec;
printf("X: %.2f, Y: %.2f\n", vec.x, vec.y);

What I'd like to do is something along the lines of this:

template<typename T>
class Vector2 : public Vector<T,2, struct { T x; T y; }> {};

template<typename T>
class Vector3 : public Vector<T,2, struct { T x; T y; T z; }> {};

and have it override a struct in the union:

template<typename T, unsigned int D, struct C>
class Vector
{
public:
    union {
        T v[D];
        // Place the passed struct here
    };
};

Is there any feasible way to do this? I do not want to use anything other than the standard library if possible. Thanks in advance.

EDIT: After reading upon all of the answers, I have understood that the way I am using unions is incorrect! Thank you to @M.M for pointing this out. I have since chosen to go a different route, but I have selected the answer that best fit what I was looking for at the time. Once again, thank you for all of the welcomed responses below!

like image 346
sheep Avatar asked Aug 26 '16 19:08

sheep


3 Answers

What you are trying to do is not allowed.
Anyway, you can do this:

template<typename T>
struct S { T x; T y; };

template<typename T>
class Vector2 : public Vector<T,2,S<T>> {};

Or this:

template<typename T>
class Vector2 : public Vector<T,2,S> {};

In the second case, Vector can be defined as:

template<typename T, unsigned int D, template<typename> class S>
class Vector {
    using MyStruct = S<T>;

    // ...

    union {
        T v[D];
        MyStruct myStruct;
    };
};
like image 75
skypjack Avatar answered Nov 20 '22 20:11

skypjack


It doesn't scale very well to a large D, but if you're just after the four to six variants I'm imagining, you could partial-specialize a base class:

#include <iostream>

template<typename T, size_t D>
struct VectorBase;

template<typename T>
struct VectorBase<T, 2>
{
    constexpr VectorBase() : v{} {}
    union {
        T v[2];
        struct { T x, y; };
    };
};

template<typename T>
struct VectorBase<T, 3>
{
    constexpr VectorBase() : v{} {}
    union {
        T v[3];
        struct { T x, y, z; };
    };
};

template<typename T>
struct VectorBase<T, 4>
{
    constexpr VectorBase() : v{} {}
    union {
        T v[4];
        struct { T x, y, z, w; };
    };
};

template<typename T, size_t D>
struct Vector : public VectorBase<T, D>
{
    using VectorBase<T, D>::v;
    using size_type = decltype(D);
    using value_type = T;

    constexpr Vector() : VectorBase<T,D>{} {}
    constexpr Vector(T scalar) {
        std::fill(std::begin(v), std::end(v), scalar);
    }

    constexpr T& operator[](size_type i) const noexcept { return v[i]; }
    constexpr const T& operator[](size_type i) noexcept { return v[i]; }

    constexpr size_type size() const noexcept { return D; }

    constexpr T* data() noexcept { return &v[0]; }
    constexpr const T* data() const noexcept { return &v[0]; }
};

template<typename T>
using Vector2 = Vector<T, 2>;
template<typename T>
using Vector3 = Vector<T, 3>;
template<typename T>
using Vector4 = Vector<T, 4>;

int main() {
    Vector3<int> v{1};

    std::cout << v[0] << ", " << v.z << "\n";
    return 0;
}

Live demo: http://ideone.com/T3QHoq

like image 2
kfsone Avatar answered Nov 20 '22 18:11

kfsone


If I understood you correctly your main purpose was to to declare order of fields that corresponds to the array elements of templated class. You cannot do this directly as templates does not accept inline type as parameter. To workaround the problem you could play with non-type template parameters to bind some labels to given index of an array:

#include <cstdio>
#include <unordered_map>
#include <utility>

struct Label { } x, y, z, w;

template <Label&... labels>
struct Pack { };

template <class, class>
struct VectorParent;

template <Label&... labels, size_t... Is>
struct VectorParent<Pack<labels...>, std::index_sequence<Is...>> {
   static std::unordered_map<Label *, size_t> label_map;
};

template <Label&... labels, size_t... Is>
std::unordered_map<Label *, size_t> VectorParent<Pack<labels...>, std::index_sequence<Is...>>::label_map = {{&labels, Is}...};

struct LabelNotFound { };

template <class T, size_t N, Label&... labels>
struct Vector:VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>> {
   static_assert(N == sizeof...(labels),
       "the cound of labels should corespond to the number of elements of the vector");
   using VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>>::label_map;
   T t[N];
   T &operator->*(Label& l) {
      auto it = label_map.find(&l);
      if (it == label_map.end())
         throw LabelNotFound{};
      return t[it->second];
   }
};

int main() {
    Vector<float,2,x,y> vec;
    vec->*x = 10.0f;
    printf("X: %.2f, Y: %.2f\n", vec->*x, vec->*y); // prints: X: 10.00, Y: 0.00
    //vec->*w = 10.1f; //would throw an exception LabelNotFound
}
like image 1
W.F. Avatar answered Nov 20 '22 18:11

W.F.