我正在学习OpenGL作为练习,并希望为它提供自己的数学库,以便使用c++ 11模板进行舒适的编程。这个问题的解决方案不应该调用运行时多态性。
基本的想法是我想要这样的东西(注意,这显然不是有效的c++代码):
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...
问题是,当它们作为数组传递给OpenGL函数时,我想将这些向量透明地转换为常规的C结构体。例如,对于n == 3
,我想将Vector<T, 3>
转换为如下形式:
template<class T>
struct Vec3 {
T v1, v2, v3;
}
这样我就可以调用
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);
并让它正常工作,就像我使用了Vec3<float>
的数组一样。我希望n == 2
, n == 3
和n == 4
也有这种行为。我不想编写3个类,并为每个类实现数学运算符。
我第一次尝试使用SFINAE和强制转换操作符:operator T()
.
// Inside Vector's declaration...
public:
operator typename std::enable_if<n == 3, Vec3<T>>::type();
这可能只适用于n == 3
,但我还需要:
operator typename std::enable_if<n == 2, Vec2<T>>::type();
operator typename std::enable_if<n == 4, Vec4<T>>::type();
和g++抱怨enable_if
没有::type
类型定义为2和4,当我实例化一个Vector<float, 3>
。
在这一点上,我使用std::array<T, n>
来保存我的值,但我意识到这并没有真正工作。这是否意味着我的值实际上不在类中,并在其他地方举行,因此传递类的数组就像传递std::array<T, n>
的数组,而不是Vec3<T>
的数组?
我目前感兴趣的领域是使用std::tuple<class... Types>
,因为它们直接将值存储在类中。这个想法有几个问题:
- 我想限制元组的类型只有一种类型,所以我的向量是同质的。
- 我希望能够获得元组的大小,而无需将其存储在我的类中。
- 我仍然需要实现
operator Vec3<T>()
和朋友。 - 我不知道是否
sizeof(tuple<float, float, float>) == sizeof(Vec3<float>)
,或者任何关于内存布局的保证,允许我安全地(说)将tuple<float, float, float>
转换为Vec3<float>
。我听说c++的stdlib在类中以相反的顺序存储元组值,例如。
std::array<T, n>
保证是T[n]
的零开销包装器,所以你可以简单地将其存储在你的类中,并确保没有填充,这可能会扰乱OpenGL调用。