vendredi 17 juillet 2015

Convert a Tuple to a Struct in a Template

I am learning OpenGL as an exercise, and want to roll my own math library for it to get comfortable programming with C++11 templates. The solution to this problem should not invoke runtime-polymorphism.

The basic idea is that I want something like this (note that this obviously is not valid C++ code):

template<class T, int n> //element type is T, size is n
class Vector {
    T v1, v2, ... , vn;
public:
    Vector(T v1, ... , T vn);
    ~Vector() noexcept;
    ...
    // more constructors and stuff here.
}

template<T, n>
Vector<T, n> operator +(Vector<T, n> lhs, Vector<T, n> rhs);

...
// more math functions and operators here...

The problem is that I want to convert these vectors into regular C structs transparently when they are passed as arrays to OpenGL functions. For example, for n == 3, I want to convert my Vector<T, 3> into something like:

template<class T>
struct Vec3 {
    T v1, v2, v3;
}

So that I can do the call:

Vector<float, 3> vertices[1];
vertices[0] = Vector<float, 3>(1.0f, 1.0f, 1.0f);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

and have it just work, as if I had used an array of Vec3<float>'s. I want this behavior for n == 2, n == 3, and n == 4. I don't want to have to write 3 classes and implement math operators for each one.

My first attempt tried to use SFINAE with the casting operator: operator T().

// Inside Vector's declaration...
public:
    operator typename std::enable_if<n == 3, Vec3<T>>::type();

This might have worked fine for only n == 3, but I also needed:

    operator typename std::enable_if<n == 2, Vec2<T>>::type();
    operator typename std::enable_if<n == 4, Vec4<T>>::type();

And g++ complained that the enable_if didn't have a ::type typedef for 2 and 4 when I instantiated a Vector<float, 3>.

At this point I was using a std::array<T, n> to hold my values, but I realized this didn't really work. Would this not mean that my values weren't actually IN the class, and held somewhere else, so that passing an array of the class would be like passting an array of std::array<T, n>, not an array of Vec3<T>?

My current area of interest is in using a std::tuple<class... Types>, since they store the values directly in the class. There are a few problems with this idea:

1) I want to restrict the type of the tuple to be only one type, so that my Vector is homogenous. 2) I want to be able to obtain the size of the tuple without storing it in my class. 3) I still need to implement operator Vec3<T>() and friends somehow. 4) I don't know if sizeof(tuple<float, float, float>) == sizeof(Vec3<float>), or any guarentees about memory layout that allows me to safely (say) cast a tuple<float, float, float> into a Vec3<float>. I've heard g++'s stdlib stores the tuple values in reverse order inside the class, for example.





Aucun commentaire:

Enregistrer un commentaire