C++ math vector template
Clash Royale CLAN TAG#URR8PPP
up vote
4
down vote
favorite
I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.
Here we go:
#ifndef Log2VECTOR_H
#define Log2VECTOR_H
#include <glm/glm.hpp>
#include <ostream>
namespace Log2
template<unsigned Dim, typename T>
class Vector
public:
/**
* Constructors
*/
Vector() = default;
Vector(const T& value)
for (unsigned i = 0; i < Dim; i++)
_data[i] = value;
template<typename ...Args>
Vector(Args... args) : _data args...
/**
* Copy constructors
*/
Vector(const Vector& other) = default;
template<typename T1>
Vector(const Vector<Dim, T1>& other)
for (unsigned i = 0; i < Dim; i++)
_data[i] = static_cast<T>(other[i]);
Vector(const Vector<Dim - 1, T>& other, T scalar)
std::memcpy(_data, other.ptr(), sizeof other);
_data[Dim - 1] = scalar;
/**
* Concatenation
*/
template<unsigned Dim1>
Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)
std::memcpy(_data, v1.ptr(), sizeof v1);
std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);
/**
* Member access
*/
inline auto & operator (unsigned index)
return _data[index];
inline const auto & operator (unsigned index) const
return _data[index];
inline const auto * ptr() const
return _data;
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const
return Vector<3, T>(_data[0], _data[1], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const
return Vector<2, T>(_data[0], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const
return Vector<2, T>(_data[0], _data[1]);
inline const auto& x() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & y() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & z() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & w() const
return _data[3];
inline const auto& r() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & g() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & b() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & a() const
return _data[3];
template<unsigned index>
inline const auto& at() const
static_assert(index < Dim, "Invalid index");
return _data[index];
template<unsigned index>
inline auto& at()
static_assert(index < Dim, "Invalid index");
return _data[index];
/**
* Vector/vector calculations
*/
inline auto operator + (const Vector& other) const
Vector result;
ComputeAdd<Dim - 1>::call(*this, other, result);
return result;
inline auto operator - (const Vector& other) const
Vector result;
ComputeSub<Dim - 1>::call(*this, other, result);
return result;
inline auto operator * (const Vector& other) const
Vector result;
ComputeMul<Dim - 1>::call(*this, other, result);
return result;
inline auto operator / (const Vector& other) const
Vector result;
ComputeDiv<Dim - 1>::call(*this, other, result);
return result;
inline auto& operator += (const Vector& other)
ComputeAdd<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator -= (const Vector& other)
ComputeSub<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator *= (const Vector& other)
ComputeMul<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator /= (const Vector& other)
ComputeDiv<Dim - 1>::call(*this, other, *this);
return *this;
/**
* Comparison operators
*/
inline auto operator == (const Vector& other) const
return ComputeEqual<Dim - 1>::call(*this, other);
inline auto operator != (const Vector& other) const
return ComputeInequal<Dim - 1>::call(*this, other);
inline auto operator < (const Vector& other) const
return ComputeLess<Dim - 1>::call(*this, other);
inline auto operator > (const Vector& other) const
return ComputeGreater<Dim - 1>::call(*this, other);
inline auto operator <= (const Vector& other) const
return ComputeLessOrEqual<Dim - 1>::call(*this, other);
inline auto operator >= (const Vector& other) const
return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);
/**
* Vector length
*/
inline auto length() const
return std::sqrt(length2());
/**
* Squared Vector length
*/
inline auto length2() const
return dot(*this, *this);
/**
* Conversion from/to glm
*/
inline Vector(const glm::vec<Dim, T>& vec)
std::memcpy(_data, &vec[0], sizeof vec);
inline operator glm::vec<Dim, T>() const
glm::vec<Dim, T> vec;
std::memcpy(&vec[0], ptr(), sizeof vec);
return vec;
/**
* Change in dimension
*/
template<unsigned N>
inline operator Vector<N, T>() const
Vector<N, T> ret;
std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
return ret;
/**
* Debug output
*/
friend std::ostream& operator << (std::ostream& os, const Vector& vec)
os << "[";
for (unsigned i = 0; i < Dim; i++)
os << vec._data[i];
if (i != Dim - 1)
os << " ";
os << "]";
return os;
private:
T _data[Dim];
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeAdd
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] + b[index];
ComputeAdd<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeAdd<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] + b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeSub
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] - b[index];
ComputeSub<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeSub<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] - b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeMul
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] * b[index];
ComputeMul<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeMul<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] * b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeDiv
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] / b[index];
ComputeDiv<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeDiv<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] / b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] == b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeInequal
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
;
template<unsigned D, typename Type>
struct ComputeInequal<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] != b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLess
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLess<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] < b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreater
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreater<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] > b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLessOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLessOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] <= b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreaterOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreaterOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] >= b[0];
;
;
using Vec2f = Vector<2, float>;
using Vec3f = Vector<3, float>;
using Vec4f = Vector<4, float>;
using Vec2u = Vector<2, unsigned>;
using Vec3u = Vector<3, unsigned>;
using Vec4u = Vector<4, unsigned>;
using Vec2i = Vector<2, int>;
using Vec3i = Vector<3, int>;
using Vec4i = Vector<4, int>;
#endif
Edit:
Definition of the dot product for code completeness:
template<unsigned Dim, typename T>
static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return ComputeDot<Dim, T, Dim - 1>::call(a, b);
template<unsigned Dim, typename T, unsigned index>
struct ComputeDot
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);
;
template<unsigned Dim, typename T>
struct ComputeDot<Dim, T, 0>
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[0] * b[0];
;
c++ template template-meta-programming coordinate-system
add a comment |Â
up vote
4
down vote
favorite
I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.
Here we go:
#ifndef Log2VECTOR_H
#define Log2VECTOR_H
#include <glm/glm.hpp>
#include <ostream>
namespace Log2
template<unsigned Dim, typename T>
class Vector
public:
/**
* Constructors
*/
Vector() = default;
Vector(const T& value)
for (unsigned i = 0; i < Dim; i++)
_data[i] = value;
template<typename ...Args>
Vector(Args... args) : _data args...
/**
* Copy constructors
*/
Vector(const Vector& other) = default;
template<typename T1>
Vector(const Vector<Dim, T1>& other)
for (unsigned i = 0; i < Dim; i++)
_data[i] = static_cast<T>(other[i]);
Vector(const Vector<Dim - 1, T>& other, T scalar)
std::memcpy(_data, other.ptr(), sizeof other);
_data[Dim - 1] = scalar;
/**
* Concatenation
*/
template<unsigned Dim1>
Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)
std::memcpy(_data, v1.ptr(), sizeof v1);
std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);
/**
* Member access
*/
inline auto & operator (unsigned index)
return _data[index];
inline const auto & operator (unsigned index) const
return _data[index];
inline const auto * ptr() const
return _data;
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const
return Vector<3, T>(_data[0], _data[1], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const
return Vector<2, T>(_data[0], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const
return Vector<2, T>(_data[0], _data[1]);
inline const auto& x() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & y() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & z() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & w() const
return _data[3];
inline const auto& r() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & g() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & b() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & a() const
return _data[3];
template<unsigned index>
inline const auto& at() const
static_assert(index < Dim, "Invalid index");
return _data[index];
template<unsigned index>
inline auto& at()
static_assert(index < Dim, "Invalid index");
return _data[index];
/**
* Vector/vector calculations
*/
inline auto operator + (const Vector& other) const
Vector result;
ComputeAdd<Dim - 1>::call(*this, other, result);
return result;
inline auto operator - (const Vector& other) const
Vector result;
ComputeSub<Dim - 1>::call(*this, other, result);
return result;
inline auto operator * (const Vector& other) const
Vector result;
ComputeMul<Dim - 1>::call(*this, other, result);
return result;
inline auto operator / (const Vector& other) const
Vector result;
ComputeDiv<Dim - 1>::call(*this, other, result);
return result;
inline auto& operator += (const Vector& other)
ComputeAdd<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator -= (const Vector& other)
ComputeSub<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator *= (const Vector& other)
ComputeMul<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator /= (const Vector& other)
ComputeDiv<Dim - 1>::call(*this, other, *this);
return *this;
/**
* Comparison operators
*/
inline auto operator == (const Vector& other) const
return ComputeEqual<Dim - 1>::call(*this, other);
inline auto operator != (const Vector& other) const
return ComputeInequal<Dim - 1>::call(*this, other);
inline auto operator < (const Vector& other) const
return ComputeLess<Dim - 1>::call(*this, other);
inline auto operator > (const Vector& other) const
return ComputeGreater<Dim - 1>::call(*this, other);
inline auto operator <= (const Vector& other) const
return ComputeLessOrEqual<Dim - 1>::call(*this, other);
inline auto operator >= (const Vector& other) const
return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);
/**
* Vector length
*/
inline auto length() const
return std::sqrt(length2());
/**
* Squared Vector length
*/
inline auto length2() const
return dot(*this, *this);
/**
* Conversion from/to glm
*/
inline Vector(const glm::vec<Dim, T>& vec)
std::memcpy(_data, &vec[0], sizeof vec);
inline operator glm::vec<Dim, T>() const
glm::vec<Dim, T> vec;
std::memcpy(&vec[0], ptr(), sizeof vec);
return vec;
/**
* Change in dimension
*/
template<unsigned N>
inline operator Vector<N, T>() const
Vector<N, T> ret;
std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
return ret;
/**
* Debug output
*/
friend std::ostream& operator << (std::ostream& os, const Vector& vec)
os << "[";
for (unsigned i = 0; i < Dim; i++)
os << vec._data[i];
if (i != Dim - 1)
os << " ";
os << "]";
return os;
private:
T _data[Dim];
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeAdd
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] + b[index];
ComputeAdd<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeAdd<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] + b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeSub
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] - b[index];
ComputeSub<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeSub<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] - b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeMul
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] * b[index];
ComputeMul<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeMul<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] * b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeDiv
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] / b[index];
ComputeDiv<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeDiv<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] / b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] == b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeInequal
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
;
template<unsigned D, typename Type>
struct ComputeInequal<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] != b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLess
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLess<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] < b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreater
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreater<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] > b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLessOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLessOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] <= b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreaterOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreaterOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] >= b[0];
;
;
using Vec2f = Vector<2, float>;
using Vec3f = Vector<3, float>;
using Vec4f = Vector<4, float>;
using Vec2u = Vector<2, unsigned>;
using Vec3u = Vector<3, unsigned>;
using Vec4u = Vector<4, unsigned>;
using Vec2i = Vector<2, int>;
using Vec3i = Vector<3, int>;
using Vec4i = Vector<4, int>;
#endif
Edit:
Definition of the dot product for code completeness:
template<unsigned Dim, typename T>
static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return ComputeDot<Dim, T, Dim - 1>::call(a, b);
template<unsigned Dim, typename T, unsigned index>
struct ComputeDot
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);
;
template<unsigned Dim, typename T>
struct ComputeDot<Dim, T, 0>
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[0] * b[0];
;
c++ template template-meta-programming coordinate-system
add a comment |Â
up vote
4
down vote
favorite
up vote
4
down vote
favorite
I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.
Here we go:
#ifndef Log2VECTOR_H
#define Log2VECTOR_H
#include <glm/glm.hpp>
#include <ostream>
namespace Log2
template<unsigned Dim, typename T>
class Vector
public:
/**
* Constructors
*/
Vector() = default;
Vector(const T& value)
for (unsigned i = 0; i < Dim; i++)
_data[i] = value;
template<typename ...Args>
Vector(Args... args) : _data args...
/**
* Copy constructors
*/
Vector(const Vector& other) = default;
template<typename T1>
Vector(const Vector<Dim, T1>& other)
for (unsigned i = 0; i < Dim; i++)
_data[i] = static_cast<T>(other[i]);
Vector(const Vector<Dim - 1, T>& other, T scalar)
std::memcpy(_data, other.ptr(), sizeof other);
_data[Dim - 1] = scalar;
/**
* Concatenation
*/
template<unsigned Dim1>
Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)
std::memcpy(_data, v1.ptr(), sizeof v1);
std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);
/**
* Member access
*/
inline auto & operator (unsigned index)
return _data[index];
inline const auto & operator (unsigned index) const
return _data[index];
inline const auto * ptr() const
return _data;
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const
return Vector<3, T>(_data[0], _data[1], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const
return Vector<2, T>(_data[0], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const
return Vector<2, T>(_data[0], _data[1]);
inline const auto& x() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & y() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & z() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & w() const
return _data[3];
inline const auto& r() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & g() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & b() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & a() const
return _data[3];
template<unsigned index>
inline const auto& at() const
static_assert(index < Dim, "Invalid index");
return _data[index];
template<unsigned index>
inline auto& at()
static_assert(index < Dim, "Invalid index");
return _data[index];
/**
* Vector/vector calculations
*/
inline auto operator + (const Vector& other) const
Vector result;
ComputeAdd<Dim - 1>::call(*this, other, result);
return result;
inline auto operator - (const Vector& other) const
Vector result;
ComputeSub<Dim - 1>::call(*this, other, result);
return result;
inline auto operator * (const Vector& other) const
Vector result;
ComputeMul<Dim - 1>::call(*this, other, result);
return result;
inline auto operator / (const Vector& other) const
Vector result;
ComputeDiv<Dim - 1>::call(*this, other, result);
return result;
inline auto& operator += (const Vector& other)
ComputeAdd<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator -= (const Vector& other)
ComputeSub<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator *= (const Vector& other)
ComputeMul<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator /= (const Vector& other)
ComputeDiv<Dim - 1>::call(*this, other, *this);
return *this;
/**
* Comparison operators
*/
inline auto operator == (const Vector& other) const
return ComputeEqual<Dim - 1>::call(*this, other);
inline auto operator != (const Vector& other) const
return ComputeInequal<Dim - 1>::call(*this, other);
inline auto operator < (const Vector& other) const
return ComputeLess<Dim - 1>::call(*this, other);
inline auto operator > (const Vector& other) const
return ComputeGreater<Dim - 1>::call(*this, other);
inline auto operator <= (const Vector& other) const
return ComputeLessOrEqual<Dim - 1>::call(*this, other);
inline auto operator >= (const Vector& other) const
return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);
/**
* Vector length
*/
inline auto length() const
return std::sqrt(length2());
/**
* Squared Vector length
*/
inline auto length2() const
return dot(*this, *this);
/**
* Conversion from/to glm
*/
inline Vector(const glm::vec<Dim, T>& vec)
std::memcpy(_data, &vec[0], sizeof vec);
inline operator glm::vec<Dim, T>() const
glm::vec<Dim, T> vec;
std::memcpy(&vec[0], ptr(), sizeof vec);
return vec;
/**
* Change in dimension
*/
template<unsigned N>
inline operator Vector<N, T>() const
Vector<N, T> ret;
std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
return ret;
/**
* Debug output
*/
friend std::ostream& operator << (std::ostream& os, const Vector& vec)
os << "[";
for (unsigned i = 0; i < Dim; i++)
os << vec._data[i];
if (i != Dim - 1)
os << " ";
os << "]";
return os;
private:
T _data[Dim];
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeAdd
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] + b[index];
ComputeAdd<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeAdd<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] + b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeSub
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] - b[index];
ComputeSub<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeSub<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] - b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeMul
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] * b[index];
ComputeMul<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeMul<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] * b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeDiv
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] / b[index];
ComputeDiv<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeDiv<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] / b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] == b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeInequal
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
;
template<unsigned D, typename Type>
struct ComputeInequal<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] != b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLess
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLess<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] < b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreater
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreater<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] > b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLessOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLessOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] <= b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreaterOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreaterOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] >= b[0];
;
;
using Vec2f = Vector<2, float>;
using Vec3f = Vector<3, float>;
using Vec4f = Vector<4, float>;
using Vec2u = Vector<2, unsigned>;
using Vec3u = Vector<3, unsigned>;
using Vec4u = Vector<4, unsigned>;
using Vec2i = Vector<2, int>;
using Vec3i = Vector<3, int>;
using Vec4i = Vector<4, int>;
#endif
Edit:
Definition of the dot product for code completeness:
template<unsigned Dim, typename T>
static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return ComputeDot<Dim, T, Dim - 1>::call(a, b);
template<unsigned Dim, typename T, unsigned index>
struct ComputeDot
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);
;
template<unsigned Dim, typename T>
struct ComputeDot<Dim, T, 0>
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[0] * b[0];
;
c++ template template-meta-programming coordinate-system
I wrote a basic template for a vector of arbitrary dimension to be used in a 3D-engine. Most operators make use of C++ template meta programming and instances are implicitly convertible to glm types. It is also possible to build a matrix class on top of this. Suggestions in terms of optimizations, safety, readability etc. are welcome.
Here we go:
#ifndef Log2VECTOR_H
#define Log2VECTOR_H
#include <glm/glm.hpp>
#include <ostream>
namespace Log2
template<unsigned Dim, typename T>
class Vector
public:
/**
* Constructors
*/
Vector() = default;
Vector(const T& value)
for (unsigned i = 0; i < Dim; i++)
_data[i] = value;
template<typename ...Args>
Vector(Args... args) : _data args...
/**
* Copy constructors
*/
Vector(const Vector& other) = default;
template<typename T1>
Vector(const Vector<Dim, T1>& other)
for (unsigned i = 0; i < Dim; i++)
_data[i] = static_cast<T>(other[i]);
Vector(const Vector<Dim - 1, T>& other, T scalar)
std::memcpy(_data, other.ptr(), sizeof other);
_data[Dim - 1] = scalar;
/**
* Concatenation
*/
template<unsigned Dim1>
Vector(const Vector<Dim1, T>& v1, const Vector<Dim - Dim1, T>& v2)
std::memcpy(_data, v1.ptr(), sizeof v1);
std::memcpy(_data + Dim1, v2.ptr(), sizeof v2);
/**
* Member access
*/
inline auto & operator (unsigned index)
return _data[index];
inline const auto & operator (unsigned index) const
return _data[index];
inline const auto * ptr() const
return _data;
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, Vector<3, T>>::type xyz() const
return Vector<3, T>(_data[0], _data[1], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xz() const
return Vector<2, T>(_data[0], _data[2]);
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, Vector<2, T>>::type xy() const
return Vector<2, T>(_data[0], _data[1]);
inline const auto& x() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & y() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & z() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & w() const
return _data[3];
inline const auto& r() const
return _data[0];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 2, T>::type const & g() const
return _data[1];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 3, T>::type const & b() const
return _data[2];
template<unsigned D = Dim>
inline typename std::enable_if<D >= 4, T>::type const & a() const
return _data[3];
template<unsigned index>
inline const auto& at() const
static_assert(index < Dim, "Invalid index");
return _data[index];
template<unsigned index>
inline auto& at()
static_assert(index < Dim, "Invalid index");
return _data[index];
/**
* Vector/vector calculations
*/
inline auto operator + (const Vector& other) const
Vector result;
ComputeAdd<Dim - 1>::call(*this, other, result);
return result;
inline auto operator - (const Vector& other) const
Vector result;
ComputeSub<Dim - 1>::call(*this, other, result);
return result;
inline auto operator * (const Vector& other) const
Vector result;
ComputeMul<Dim - 1>::call(*this, other, result);
return result;
inline auto operator / (const Vector& other) const
Vector result;
ComputeDiv<Dim - 1>::call(*this, other, result);
return result;
inline auto& operator += (const Vector& other)
ComputeAdd<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator -= (const Vector& other)
ComputeSub<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator *= (const Vector& other)
ComputeMul<Dim - 1>::call(*this, other, *this);
return *this;
inline auto& operator /= (const Vector& other)
ComputeDiv<Dim - 1>::call(*this, other, *this);
return *this;
/**
* Comparison operators
*/
inline auto operator == (const Vector& other) const
return ComputeEqual<Dim - 1>::call(*this, other);
inline auto operator != (const Vector& other) const
return ComputeInequal<Dim - 1>::call(*this, other);
inline auto operator < (const Vector& other) const
return ComputeLess<Dim - 1>::call(*this, other);
inline auto operator > (const Vector& other) const
return ComputeGreater<Dim - 1>::call(*this, other);
inline auto operator <= (const Vector& other) const
return ComputeLessOrEqual<Dim - 1>::call(*this, other);
inline auto operator >= (const Vector& other) const
return ComputeGreaterOrEqual<Dim - 1>::call(*this, other);
/**
* Vector length
*/
inline auto length() const
return std::sqrt(length2());
/**
* Squared Vector length
*/
inline auto length2() const
return dot(*this, *this);
/**
* Conversion from/to glm
*/
inline Vector(const glm::vec<Dim, T>& vec)
std::memcpy(_data, &vec[0], sizeof vec);
inline operator glm::vec<Dim, T>() const
glm::vec<Dim, T> vec;
std::memcpy(&vec[0], ptr(), sizeof vec);
return vec;
/**
* Change in dimension
*/
template<unsigned N>
inline operator Vector<N, T>() const
Vector<N, T> ret;
std::memcpy(&ret[0], ptr(), std::min(sizeof ret, sizeof *this));
return ret;
/**
* Debug output
*/
friend std::ostream& operator << (std::ostream& os, const Vector& vec)
os << "[";
for (unsigned i = 0; i < Dim; i++)
os << vec._data[i];
if (i != Dim - 1)
os << " ";
os << "]";
return os;
private:
T _data[Dim];
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeAdd
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] + b[index];
ComputeAdd<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeAdd<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] + b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeSub
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] - b[index];
ComputeSub<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeSub<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] - b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeMul
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] * b[index];
ComputeMul<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeMul<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] * b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeDiv
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector <D, Type>& result)
result[index] = a[index] / b[index];
ComputeDiv<index - 1, D, Type>::call(a, b, result);
;
template<unsigned D, typename Type>
struct ComputeDiv<0, D, Type>
static inline void call(const Vector<D, Type>& a, const Vector<D, Type>& b, Vector<D, Type>& result)
result[0] = a[0] / b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] == b[index] && ComputeEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] == b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeInequal
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
;
template<unsigned D, typename Type>
struct ComputeInequal<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] != b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLess
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] < b[index] && ComputeLess<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLess<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] < b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreater
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] > b[index] && ComputeGreater<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreater<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] > b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeLessOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] <= b[index] && ComputeLessOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeLessOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] <= b[0];
;
template<unsigned index, unsigned D = Dim, typename Type = T>
struct ComputeGreaterOrEqual
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[index] >= b[index] && ComputeGreaterOrEqual<index - 1, D, Type>::call(a, b);
;
template<unsigned D, typename Type>
struct ComputeGreaterOrEqual<0, D, Type>
static inline auto call(const Vector<D, Type>& a, const Vector<D, Type>& b)
return a[0] >= b[0];
;
;
using Vec2f = Vector<2, float>;
using Vec3f = Vector<3, float>;
using Vec4f = Vector<4, float>;
using Vec2u = Vector<2, unsigned>;
using Vec3u = Vector<3, unsigned>;
using Vec4u = Vector<4, unsigned>;
using Vec2i = Vector<2, int>;
using Vec3i = Vector<3, int>;
using Vec4i = Vector<4, int>;
#endif
Edit:
Definition of the dot product for code completeness:
template<unsigned Dim, typename T>
static inline T dot(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return ComputeDot<Dim, T, Dim - 1>::call(a, b);
template<unsigned Dim, typename T, unsigned index>
struct ComputeDot
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[index] * b[index] + ComputeDot<Dim, T, index - 1>::call(a, b);
;
template<unsigned Dim, typename T>
struct ComputeDot<Dim, T, 0>
static inline T call(const Vector<Dim, T>& a, const Vector<Dim, T>& b)
return a[0] * b[0];
;
c++ template template-meta-programming coordinate-system
c++ template template-meta-programming coordinate-system
edited 19 mins ago
asked 3 hours ago
Philipp FleiÃner
434
434
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
A few issues:
- Functions defined in the class in the header are
inline
by default, so there's no need to use the keyword everywhere. - Use
std::copy
, notstd::memcpy
. It's just as fast (if not faster), and doesn't havememcpy
's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable). ComputeAdd
etc. could be implemented with simple for loops.- Certain operators can be implemented in terms of other operators to save on code duplication (e.g.
==
/!=
). - Returning by value with
xyz()
etc., and then returning a reference withx()
etc. is potentially error prone. I'd suggest uniformly returning a copy. - It might be useful to have a non-const version of
ptr()
. - The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?
- The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.
- Conversion operators should be
explicit
, to prevent unhappy accidents. One argument constructors should also be explicit. - A static
size()
member function to return the number of dimensions might be useful. - Iterator support might be useful.
- Using
std::array
instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
A few issues:
- Functions defined in the class in the header are
inline
by default, so there's no need to use the keyword everywhere. - Use
std::copy
, notstd::memcpy
. It's just as fast (if not faster), and doesn't havememcpy
's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable). ComputeAdd
etc. could be implemented with simple for loops.- Certain operators can be implemented in terms of other operators to save on code duplication (e.g.
==
/!=
). - Returning by value with
xyz()
etc., and then returning a reference withx()
etc. is potentially error prone. I'd suggest uniformly returning a copy. - It might be useful to have a non-const version of
ptr()
. - The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?
- The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.
- Conversion operators should be
explicit
, to prevent unhappy accidents. One argument constructors should also be explicit. - A static
size()
member function to return the number of dimensions might be useful. - Iterator support might be useful.
- Using
std::array
instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...
add a comment |Â
up vote
1
down vote
A few issues:
- Functions defined in the class in the header are
inline
by default, so there's no need to use the keyword everywhere. - Use
std::copy
, notstd::memcpy
. It's just as fast (if not faster), and doesn't havememcpy
's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable). ComputeAdd
etc. could be implemented with simple for loops.- Certain operators can be implemented in terms of other operators to save on code duplication (e.g.
==
/!=
). - Returning by value with
xyz()
etc., and then returning a reference withx()
etc. is potentially error prone. I'd suggest uniformly returning a copy. - It might be useful to have a non-const version of
ptr()
. - The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?
- The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.
- Conversion operators should be
explicit
, to prevent unhappy accidents. One argument constructors should also be explicit. - A static
size()
member function to return the number of dimensions might be useful. - Iterator support might be useful.
- Using
std::array
instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...
add a comment |Â
up vote
1
down vote
up vote
1
down vote
A few issues:
- Functions defined in the class in the header are
inline
by default, so there's no need to use the keyword everywhere. - Use
std::copy
, notstd::memcpy
. It's just as fast (if not faster), and doesn't havememcpy
's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable). ComputeAdd
etc. could be implemented with simple for loops.- Certain operators can be implemented in terms of other operators to save on code duplication (e.g.
==
/!=
). - Returning by value with
xyz()
etc., and then returning a reference withx()
etc. is potentially error prone. I'd suggest uniformly returning a copy. - It might be useful to have a non-const version of
ptr()
. - The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?
- The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.
- Conversion operators should be
explicit
, to prevent unhappy accidents. One argument constructors should also be explicit. - A static
size()
member function to return the number of dimensions might be useful. - Iterator support might be useful.
- Using
std::array
instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...
A few issues:
- Functions defined in the class in the header are
inline
by default, so there's no need to use the keyword everywhere. - Use
std::copy
, notstd::memcpy
. It's just as fast (if not faster), and doesn't havememcpy
's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable). ComputeAdd
etc. could be implemented with simple for loops.- Certain operators can be implemented in terms of other operators to save on code duplication (e.g.
==
/!=
). - Returning by value with
xyz()
etc., and then returning a reference withx()
etc. is potentially error prone. I'd suggest uniformly returning a copy. - It might be useful to have a non-const version of
ptr()
. - The default constructor will not initialize values in the array. Is a (potential and minor) performance gain in certain circumstances really worth the massive, omnipresent risk?
- The template parameter pack constructor will work for numbers of arguments smaller than the dimensions of the vector, and initialize any non-explicitly initialized values implicitly (i.e. to zero). This could be confusing.
- Conversion operators should be
explicit
, to prevent unhappy accidents. One argument constructors should also be explicit. - A static
size()
member function to return the number of dimensions might be useful. - Iterator support might be useful.
- Using
std::array
instead of a raw c-style array might fix some of the above or make implementing them (e.g. iterators) easier...
edited 15 mins ago
answered 21 mins ago
user673679
1,505620
1,505620
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f205553%2fc-math-vector-template%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password