# A simple Vector implementation

Just made a simple Vec class, nothing fancy but I am open to suggestions for improvements. Of course, this is not suppose to replace or used for the same tasks as std::vector. This is more like opencv's Vec and eventually I want to include this in a bigger project of mine.

#include <cmath>
#include <vector>
#include <initializer_list>
#include <cassert>

#define VEC_ASSERT(x) assert(x)

template<typename T, unsigned int C>
class Vec {

public:

typedef T           dataType;
typedef T&          dataType_ref;
typedef const T&    dataType_cref;

// Empty Constructor
Vec();

// Single-Arg Constructor
explicit Vec(T v);

// From std::vector Constructor
Vec(const std::vector<T>& v);

// From std::initializer_list Constructor
Vec(const std::initializer_list<T>& l);

// Main Constructor
template<typename ... Args>
explicit Vec(T v, Args&& ... args);

// Get vector dimensions
unsigned int dim() const;

// Get vector length
double length() const;

// Get vectors dist
double dist(const Vec<T, C>& v) const;

// Get the cross product (3D Vectors only)
Vec<T, C> cross(const Vec<T, C>& v) const;

// Get the dot product
double dot(const Vec<T, C>& v) const;

// Get ortho vector (2D vectors only)
Vec<T, C> ortho() const;

// Normalize vector values
Vec<T, C> norm() const;

// Rotate (2D Vectors only)
Vec<T, C> rotate(double angle) const;

// Rotate on x-axis (3D Vectors only)
Vec<T, C> rotateX(double angle) const;

// Rotate on y-axis (3D Vectors only)
Vec<T, C> rotateY(double angle) const;

// Rotate on z-axis (3D Vectors only)
Vec<T, C> rotateZ(double angle) const;

// Convert to std::vector
std::vector<dataType> to_std_vector() const;

// Cast
template<typename TT, unsigned int CC = C>
Vec<TT, CC> to() const;

// Access vector values
dataType_ref operator[](int index);
dataType_ref operator()(int index);
dataType_cref operator[](int index) const;
dataType_cref operator()(int index) const;

// Vector Operations with Scalars
Vec<T, C> operator+(T v);
Vec<T, C> operator-(T v);
Vec<T, C> operator*(T v);
Vec<T, C> operator/(T v);
Vec<T, C>& operator+=(T v);
Vec<T, C>& operator-=(T v);
Vec<T, C>& operator*=(T v);
Vec<T, C>& operator/=(T v);

// Vector Operations with Vectors
Vec<T, C> operator+(const Vec<T, C>& v);
Vec<T, C> operator-(const Vec<T, C>& v);
Vec<T, C> operator*(const Vec<T, C>& v);
Vec<T, C> operator/(const Vec<T, C>& v);

private:

// Recursive pusher (used by constructor)
template<typename ... Args>
void push(T v, Args&& ... args);

// Base pusher
void push(T v);

// Vector values
dataType values[C];

// Index for Vector pusher
unsigned int idx;

};

template<typename T, unsigned int C>
Vec<T, C>::Vec() {

for ( unsigned int i = 0; i < C; ++i )
this->values[i] = 0;

}

template<typename T, unsigned int C>
Vec<T, C>::Vec(T v) {

for ( unsigned int i = 0; i < C; ++i )
this->values[i] = v;

}

template<typename T, unsigned int C>
Vec<T, C>::Vec(const std::vector<T>& v) {

VEC_ASSERT(v.size() <= C);

for ( unsigned i = 0; i < v.size(); ++i )
this->values[i] = v[i];

}

template<typename T, unsigned int C>
Vec<T, C>::Vec(const std::initializer_list<T>& l) {

VEC_ASSERT(l.size() <= C);

unsigned i = 0;
for ( auto it : l )
this->values[i++] = it;

}

template<typename T, unsigned int C>
template<typename ... Args>
Vec<T, C>::Vec(T v, Args&& ... args) {

this->idx = 0;
this->values[idx] = v;
this->push(args ...);

}

template<typename T, unsigned int C>
template<typename ... Args>
void Vec<T, C>::push(T v, Args&& ... args) {

this->values[++(this->idx)] = v;
this->push(args ...);

}

template<typename T, unsigned int C>
void Vec<T, C>::push(T v) {

VEC_ASSERT(this->idx + 1 < C);

this->values[++(this->idx)] = v;

}

template<typename T, unsigned int C>
unsigned int Vec<T, C>::dim() const {

return C;

}

template<typename T, unsigned int C>
double Vec<T, C>::length() const {

double result = 0;

for ( unsigned int i = 0; i < C; ++i )
result += this->values[i] * this->values[i];

return std::sqrt(result);

}

template<typename T, unsigned int C>
double Vec<T, C>::dist(const Vec<T, C>& v) const {

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] - v[i];

return result.length();

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::cross(const Vec<T, C>& v) const {

VEC_ASSERT(C == 3);

Vec<T, C> result;

result[0] = this->values[1] * v[2] - this->values[2] * v[1];
result[1] = this->values[0] * v[2] - this->values[2] * v[0];
result[2] = this->values[0] * v[0] - this->values[1] * v[0];

return result;

}

template<typename T, unsigned int C>
double Vec<T, C>::dot(const Vec<T, C>& v) const {

double result = 0.0;

for ( unsigned int i = 0; i < C; ++i )
result += this->values[i] * v[i];

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::ortho() const {

VEC_ASSERT(C == 2);

return Vec<T, C>(this->values[1], -(this->values[0]));

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::norm() const {

VEC_ASSERT(this->length() != 0);

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] * (1.0 / this->length());

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::rotate(double angle) const {

VEC_ASSERT(C == 2);

double theta = angle / 180.0 * M_PI;
double c = std::cos(theta);
double s = std::sin(theta);
double x = this->values[0] * c - this->values[1] * s;
double y = this->values[0] * s + this->values[1] * c;

return Vec<T, C>(x, y);

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::rotateX(double angle) const {

VEC_ASSERT(C == 3);

double theta = angle / 180.0 * M_PI;
double c = std::cos(theta);
double s = std::sin(theta);
double x = this->values[0];
double y = this->values[1] * c - this->values[2] * s;
double z = this->values[1] * s + this->values[2] * c;

return Vec<T, C>(x, y, z);

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::rotateY(double angle) const {

VEC_ASSERT(C == 3);

double theta = angle / 180.0 * M_PI;
double c = std::cos(theta);
double s = std::sin(theta);
double x = this->values[0] * c + this->values[2] * s;
double y = this->values[1];
double z = -(this->values[0]) * s + this->values[2] * c;

return Vec<T, C>(x, y, z);

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::rotateZ(double angle) const {

VEC_ASSERT(C == 3);

double theta = angle / 180.0 * M_PI;
double c = std::cos(theta);
double s = std::sin(theta);
double x = this->values[0] * c - this->values[1] * s;
double y = this->values[0] * s + this->values[1] * c;
double z = this->values[2];

return Vec<T, C>(x, y, z);

}

template<typename T, unsigned int C>
auto Vec<T, C>::to_std_vector() const -> std::vector<dataType> {

return std::vector<dataType>(&this->values[0], &this->values[0] + C);

}

template<typename T, unsigned int C>
template<typename TT, unsigned int CC>
Vec<TT, CC> Vec<T, C>::to() const {

Vec<TT, CC> result;

for ( unsigned int i = 0; i < std::min(C, CC); ++i )
result[i] = static_cast<TT>(this->values[i]);

return result;

}

template<typename T, unsigned int C>
auto Vec<T, C>::operator[](int index) -> dataType_ref {

VEC_ASSERT(index < C);

return this->values[index];

}

template<typename T, unsigned int C>
auto Vec<T, C>::operator()(int index) -> dataType_ref {

VEC_ASSERT(index < C);

return this->values[index];

}

template<typename T, unsigned int C>
auto Vec<T, C>::operator[](int index) const -> dataType_cref {

VEC_ASSERT(index < C);

return this->values[index];

}

template<typename T, unsigned int C>
auto Vec<T, C>::operator()(int index) const -> dataType_cref {

VEC_ASSERT(index < C);

return this->values[index];

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator+(T v) {

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] + v;

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator-(T v) {

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] - v;

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator*(T v) {

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] * v;

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator/(T v) {

VEC_ASSERT(v != 0);

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] / v;

return result;

}

template<typename T, unsigned int C>
Vec<T, C>& Vec<T, C>::operator+=(T v) {

for ( unsigned int i = 0; i < C; ++i )
this->values[i] += v;

return *this;

}

template<typename T, unsigned int C>
Vec<T, C>& Vec<T, C>::operator-=(T v) {

for ( unsigned int i = 0; i < C; ++i )
this->values[i] -= v;

return *this;

}

template<typename T, unsigned int C>
Vec<T, C>& Vec<T, C>::operator*=(T v) {

for ( unsigned int i = 0; i < C; ++i )
this->values[i] *= v;

return *this;

}

template<typename T, unsigned int C>
Vec<T, C>& Vec<T, C>::operator/=(T v) {

VEC_ASSERT(v != 0);

for ( unsigned int i = 0; i < C; ++i )
this->values[i] /= v;

return *this;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator+(const Vec<T, C>& v) {

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] + v[i];

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator-(const Vec<T, C>& v) {

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] - v[i];

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator*(const Vec<T, C>& v) {

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] * v[i];

return result;

}

template<typename T, unsigned int C>
Vec<T, C> Vec<T, C>::operator/(const Vec<T, C>& v) {

for ( unsigned int i = 0; i < C; ++i )
VEC_ASSERT(v[i] != 0);

Vec<T, C> result;

for ( unsigned int i = 0; i < C; ++i )
result[i] = this->values[i] / v[i];

return result;

}

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;

typedef Vec<unsigned int, 2> Vec2u;
typedef Vec<unsigned int, 3> Vec3u;
typedef Vec<unsigned int, 4> Vec4u;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;

typedef Vec<char, 2> Vec2c;
typedef Vec<char, 3> Vec3c;
typedef Vec<char, 4> Vec4c;

typedef Vec<unsigned char, 2> Vec2uc;
typedef Vec<unsigned char, 3> Vec3uc;
typedef Vec<unsigned char, 4> Vec4uc;

• @SamOnela Sure, I will change it back. – DimChtz Aug 24 '18 at 19:51
• I already did that. – Sᴀᴍ Onᴇᴌᴀ Aug 24 '18 at 19:51
• @SamOnela Okay. I just thought I should include what rsy56640 suggested. – DimChtz Aug 24 '18 at 19:52
• Which C++ version are you using? Can I assume C++17? – Rakete1111 Aug 25 '18 at 9:29

Finally a real vector class :P.

1. Any reason why you use VEC_ASSERT instead of just assert. I don't really see the advantage of doing so.

2. using declarations are nicer than typedefs IMO:

using dataType = T;

3. If you use exceptions, mark functions that don't or shouldn't throw noexcept.

4. Make use of the injected-class-name:

Vec cross(const Vec &v) const;

5. You should implement all the @= operators for vectors. Also unary -.

6. Use standard algorithms:

std::fill(values, values + C, 0); // default constructor, second one too
std::copy(v.begin(), v.end(), values); // std::vector constructor, init list
std::inner_product(v.begin(), v.end(), v.begin(), 0); // length/dot
std::transform(values, values + C, result.values, [length](const auto& value) {
return values / length;
}); // norm, operator@
assert(std::all_of(v.values, v.values + C, [](const auto& value) {
return value != 0;
})); // operator/

7. You don't have to use this-> everywhere you know :).

8. You should consider using assert messages: assert(v[0] == 0 && "the first element must be 0!");

9. No love for long double and signed char? They don't have aliases.

10. You don't fill the rest of the elements to 0 in your std::vector, initializer list constructors and in to.

11. You don't need push by changing your signature a bit and doing:

template <typename T, unsigned int C>
template <typename... Args>
Vec<T, C>::Vec(Args&&... args) : Vec{std::forward<Args>(args)...} {
static_assert(sizeof...(Args) <= C, "too many arguments to vector");
}

12. There are techniques to avoid implementing similar code between operator@ and operator@=:

friend Foo operator+(Foo lhs, const Foo& rhs) {
lhs += rhs;
return lhs;
}
Foo& operator+=(const Foo& rhs) const {
// do logic
return *this;
}


13. If you want you can reduce the code duplicate of cv qualified operator[]s by using const_cast.

14. Please prohibit creating a vector of length 0! :)

15. You could provide rvalue overloads of operator[] to enable efficient moving, but you don't have to. This is overkill, mostly used in the standard library. Have a look at std::optional::operator* to see this in action.

16. You might want to consider adding a constructor that takes a pair of iterators, so that Vec can be initialized by anything really, not just a std::vector.

17. Consider making everything constexpr.

• Thanks a lot for all your suggestions. 1) No, there was something else I wanted to do and I didn't, unfortunately I can't re-post the code. 2) I could use using, 3) yes, I will use exceptions, 4) does this work for the implementation as well? 5) if you mean ==, !=, <, > etc, I will. I am just thinking 2 possible ways: a) == returns true or false if both vectors are the same or not and b) == returns a vector of bools (like matlab does) 6) yes, I should and I will, 7) I know, it's just my style, 8) I am still thinking if I should go with assert or exceptions or both (any suggestion)? – DimChtz Aug 25 '18 at 11:12
• @DimChtz 5) Oh yeah that too. I meant Vec& operator+=(const Vec&) – Rakete1111 Aug 25 '18 at 11:14
• 9) Yes, eventually I will add more aliases, 10) True, I will fix this, 11) Nice, I didn't know this, 12) will do that too, 13) i didn't think of that :P, 14) I was thinking of that, should I use enable_if?, 15) I think I lost you here :P, 16) I will – DimChtz Aug 25 '18 at 11:15
• @DimChz Use asserts for precondition violation. I'd use asserts in your case, because they should be guaranteed by the caller. – Rakete1111 Aug 25 '18 at 11:15
• Yes, I will eventually include everything. I am still doing changes – DimChtz Aug 25 '18 at 11:16
template<typename T, unsigned int C>
class Vec;


The template generates redundant codes if C varies for the same T.
You can look through <<Effective C++>> Item 44 and refactor like this:

template<typename T>
class VecBase {
T* data;
public:
VecBase(std::size_t C);
};

template<typename T, unsigned int C>
class Vec :VecBase<T> {
public:
Vec() :VecBase<T>(C)
{}
};


The benefit is:

• prevent software from being too large.
• shrink the size of Vec<T, C> (data is allocated on the heap, just contains a pointer)
• Thanks for your answer. Should I use VecBase only for storing the data and keep everything else inside Vec? – DimChtz Aug 24 '18 at 14:44
• Right, and remember to correctly implement copy/move/dtor. – rsy56640 Aug 24 '18 at 15:06
• I think this: Vec() :VecBase(C) should be: Vec() : VecBase<T>(C). – DimChtz Aug 24 '18 at 18:35
• Please don't use the term heap it is not meaningful in a C++ context (Don't be fooled because Java uses these terms). There is no reference to heap/stack in the C++ standard for a good reason. The terms you should be using are automatic/static/dynamic storage duration objects. Here the difference is between automatic and dynamic objects. – Martin York Aug 28 '18 at 6:25