Skip to content

Instantly share code, notes, and snippets.

@znnahiyan
Last active February 26, 2021 19:09
Show Gist options
  • Save znnahiyan/5423cad65c5541a0a1ee9b959419d979 to your computer and use it in GitHub Desktop.
Save znnahiyan/5423cad65c5541a0a1ee9b959419d979 to your computer and use it in GitHub Desktop.
Standalone C++20 3D vectors class (templated and with arithmetic operations defined)
#include <iostream>
#include "Vec3.hpp"
int main() {
Vec3<int> first = {1, 2, 3};
Vec3<int> second = first + 2;
Vec3<double> third = first + 1.5;
Vec3<double> fourth = 2.5 + first;
Vec3<double> fifth = {1.0, 2.0, 3.0};
cout << first << endl;
cout << second << endl;
cout << third << endl;
cout << fourth << endl;
cout << fifth << endl;
cout << fourth + fifth << endl;
return 0;
}
#ifndef VEC3_HPP
#define VEC3_HPP
#include <iostream>
#include <numbers> // C++20
#include <concepts> // C++20
#include <type_traits>
#include <tuple> // C++14
// Concepts for making sure numeric promotion works: int*float -> float, etc.
// WARNING: The static check (std::is_Arithmetic<...>) only matches fundamental types (e.g. int, float),
// but not actually std::complex or any other type which implements the operations: +, -, *, /.
template <typename T>
concept Arithmetic = std::is_arithmetic<T>::value;
// Concept to ensure that elements can be printed.
template <typename T, typename _Elem = char, typename _Traits = std::char_traits<char>>
concept Printable = requires (std::basic_ostream<_Elem, _Traits>& os, T a) {
os << a;
};
template <typename T = double> requires Arithmetic<T> && Printable<T>
struct Vec3 {
T x, y, z;
// Use special member functions to autogenerate copy ctor, move ctor, copy =, and move =.
Vec3() = default;
template <Arithmetic U> Vec3(U x, U y, U z) : x(x), y(y), z(z) {}
template <Arithmetic U> Vec3(const Vec3<U>& rhs) : x(rhs.x), y(rhs.y), z(rhs.z) {}
// Vector Arithmetic.
template <Arithmetic U> Vec3<T>& operator+=(const Vec3<U>& rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return *this; }
template <Arithmetic U> Vec3<T>& operator-=(const Vec3<U>& rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; }
template <Arithmetic U> Vec3<decltype((T()+U()))> operator+(const Vec3<U>& rhs) const { return static_cast<Vec3<decltype((T()+U()))>>(*this) += rhs; }
template <Arithmetic U> Vec3<decltype((T()-U()))> operator-(const Vec3<U>& rhs) const { return static_cast<Vec3<decltype((T()-U()))>>(*this) -= rhs; }
// Scalar Arithmetic. NOTE: Allows broadcasting from scalars to vectors for + and -.
template <Arithmetic U> Vec3<T>& operator+=(const U& rhs) { x += rhs; y += rhs; z += rhs; return *this; }
template <Arithmetic U> Vec3<T>& operator-=(const U& rhs) { x -= rhs; y -= rhs; z -= rhs; return *this; }
template <Arithmetic U> Vec3<T>& operator*=(const U& rhs) { x *= rhs; y *= rhs; z *= rhs; return *this; }
template <Arithmetic U> Vec3<T>& operator/=(const U& rhs) { x /= rhs; y /= rhs; z /= rhs; return *this; }
// Comparison.
template <Arithmetic U>
bool operator==(const Vec3<U>& rhs) const {
return std::tie(x, y, z) == std::tie(rhs.x, rhs.y, rhs.z);
};
// Inner product.
template <Arithmetic U>
decltype((T()*U()+T()*U()+T()*U())) operator*(const Vec3<U>& rhs) const {
return x*rhs.x + y*rhs.y + z*rhs.z;
}
// Outer product. (Not implemented)
template <Arithmetic U>
void operator^(const Vec3<U>& rhs) = delete;
// Norm.
decltype((std::sqrt(T()*T()+T()*T()+T()*T()))) norm() const { return std::sqrt(this->operator*(*this)); }
// Printing.
friend std::ostream& operator<<(std::ostream& os, const Vec3<T>& vec) {
return os << "Vec3<" << typeid(T).name() << ">(x=" << vec.x << ", y=" << vec.y << ", z=" << vec.z << ')';
}
};
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()+U()))> operator+(const Vec3<T>& lhs, const U& rhs) { return static_cast<Vec3<decltype((T()+U()))>>(lhs) += rhs; }
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()-U()))> operator-(const Vec3<T>& lhs, const U& rhs) { return static_cast<Vec3<decltype((T()-U()))>>(lhs) -= rhs; }
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()*U()))> operator*(const Vec3<T>& lhs, const U& rhs) { return static_cast<Vec3<decltype((T()*U()))>>(lhs) *= rhs; }
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()/U()))> operator/(const Vec3<T>& lhs, const U& rhs) { return static_cast<Vec3<decltype((T()/U()))>>(lhs) /= rhs; }
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()+U()))> operator+(const U& lhs, const Vec3<T>& rhs) { return rhs + lhs; }
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()-U()))> operator-(const U& lhs, const Vec3<T>& rhs) { return rhs - lhs; }
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()*U()))> operator*(const U& lhs, const Vec3<T>& rhs) { return rhs * lhs; }
template <Arithmetic T, Arithmetic U> Vec3<decltype((T()/U()))> operator/(const U& lhs, const Vec3<T>& rhs) { return rhs / lhs; }
#endif // VEC3_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment