Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 130 additions & 126 deletions src/core/include/mp-units/cartesian_vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <mp-units/bits/module_macros.h>
#include <mp-units/framework/customization_points.h>
#include <mp-units/framework/representation_concepts.h>
#include <type_traits>
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is not needed (included below). Also, this is not a correct section to include this file as it will collide with import std; if used.


#if MP_UNITS_HOSTED
#include <mp-units/bits/fmt.h>
Expand All @@ -37,8 +38,10 @@
import std;
#else
#include <cmath>
#include <concepts>
#include <cstddef>
#include <type_traits>
#include <utility>
#if MP_UNITS_HOSTED
#include <ostream>
#endif
Expand All @@ -50,104 +53,18 @@ namespace mp_units {
MP_UNITS_EXPORT template<detail::Scalar T>
class cartesian_vector;

namespace detail {

struct cartesian_vector_iface {
template<typename T, typename U>
requires requires(T t, U u) { t + u; }
[[nodiscard]] friend constexpr auto operator+(const cartesian_vector<T>& lhs, const cartesian_vector<U>& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] + rhs._coordinates_[0],
lhs._coordinates_[1] + rhs._coordinates_[1],
lhs._coordinates_[2] + rhs._coordinates_[2]};
}

template<typename T, typename U>
requires requires(T t, U u) { t - u; }
[[nodiscard]] friend constexpr auto operator-(const cartesian_vector<T>& lhs, const cartesian_vector<U>& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] - rhs._coordinates_[0],
lhs._coordinates_[1] - rhs._coordinates_[1],
lhs._coordinates_[2] - rhs._coordinates_[2]};
}

template<typename T, typename U>
requires requires(T t, U u) { t * u; }
[[nodiscard]] friend constexpr auto operator*(const cartesian_vector<T>& lhs, const U& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] * rhs, lhs._coordinates_[1] * rhs,
lhs._coordinates_[2] * rhs};
}

template<typename T, typename U>
requires requires(T t, U u) { t * u; }
[[nodiscard]] friend constexpr auto operator*(const T& lhs, const cartesian_vector<U>& rhs)
{
return rhs * lhs;
}

template<typename T, typename U>
requires requires(T t, U u) { t / u; }
[[nodiscard]] friend constexpr auto operator/(const cartesian_vector<T>& lhs, const U& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] / rhs, lhs._coordinates_[1] / rhs,
lhs._coordinates_[2] / rhs};
}

template<typename T, std::equality_comparable_with<T> U>
[[nodiscard]] friend constexpr bool operator==(const cartesian_vector<T>& lhs, const cartesian_vector<U>& rhs)
{
return lhs._coordinates_[0] == rhs._coordinates_[0] && lhs._coordinates_[1] == rhs._coordinates_[1] &&
lhs._coordinates_[2] == rhs._coordinates_[2];
}

template<typename T, typename U>
requires requires(T t, U u, decltype(t * u) v) {
t * u;
v + v;
}
[[nodiscard]] friend constexpr auto scalar_product(const cartesian_vector<T>& lhs, const cartesian_vector<U>& rhs)
{
return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] +
lhs._coordinates_[2] * rhs._coordinates_[2];
}

template<typename T, typename U>
requires requires(T t, U u, decltype(t * u) v) {
t * u;
v - v;
}
[[nodiscard]] friend constexpr auto vector_product(const cartesian_vector<T>& lhs, const cartesian_vector<U>& rhs)
{
return ::mp_units::cartesian_vector{
lhs._coordinates_[1] * rhs._coordinates_[2] - lhs._coordinates_[2] * rhs._coordinates_[1],
lhs._coordinates_[2] * rhs._coordinates_[0] - lhs._coordinates_[0] * rhs._coordinates_[2],
lhs._coordinates_[0] * rhs._coordinates_[1] - lhs._coordinates_[1] * rhs._coordinates_[0]};
}
};

} // namespace detail

MP_UNITS_EXPORT template<detail::Scalar T = double>
class cartesian_vector : public detail::cartesian_vector_iface {
class cartesian_vector {
public:
// public members required to satisfy structural type requirements :-(
T _coordinates_[3];
using value_type = T;

template<typename... Args>
requires(... && std::constructible_from<T, Args>)
constexpr explicit(!(... && std::convertible_to<Args, T>)) cartesian_vector(Args&&... args) :
_coordinates_{static_cast<T>(std::forward<Args>(args))...}
{
}
constexpr cartesian_vector() = default;

template<typename U>
requires std::constructible_from<T, U>
constexpr explicit(!std::convertible_to<U, T>) cartesian_vector(const cartesian_vector<U>& other) :
_coordinates_{static_cast<T>(other[0]), static_cast<T>(other[1]), static_cast<T>(other[2])}
{
}
explicit constexpr cartesian_vector(T x) : _coordinates_{x, T{}, T{}} {}
constexpr cartesian_vector(T x, T y) : _coordinates_{x, y, T{}} {}
constexpr cartesian_vector(T x, T y, T z) : _coordinates_{x, y, z} {}

template<typename U>
requires std::constructible_from<T, U>
Expand All @@ -157,23 +74,6 @@ class cartesian_vector : public detail::cartesian_vector_iface {
{
}

template<std::convertible_to<T> U>
constexpr cartesian_vector& operator=(const cartesian_vector<U>& other)
{
_coordinates_[0] = other[0];
_coordinates_[1] = other[1];
_coordinates_[2] = other[2];
return *this;
}

template<std::convertible_to<T> U>
constexpr cartesian_vector& operator=(cartesian_vector<U>&& other)
{
_coordinates_[0] = std::move(other[0]);
_coordinates_[1] = std::move(other[1]);
_coordinates_[2] = std::move(other[2]);
return *this;
}
Comment on lines -160 to -176
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you delete those?


[[nodiscard]] constexpr T magnitude() const
requires treat_as_floating_point<T>
Expand All @@ -198,11 +98,11 @@ class cartesian_vector : public detail::cartesian_vector_iface {
[[nodiscard]] constexpr cartesian_vector operator+() const { return *this; }
[[nodiscard]] constexpr cartesian_vector operator-() const
{
return {-_coordinates_[0], -_coordinates_[1], -_coordinates_[2]};
return cartesian_vector{-_coordinates_[0], -_coordinates_[1], -_coordinates_[2]};
Comment on lines -201 to +101
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed.

}

template<typename U>
requires requires(T t, U u) {
requires requires(T& t, const U& u) {
{ t += u } -> std::same_as<T&>;
}
constexpr cartesian_vector& operator+=(const cartesian_vector<U>& other)
Expand All @@ -214,7 +114,7 @@ class cartesian_vector : public detail::cartesian_vector_iface {
}

template<typename U>
requires requires(T t, U u) {
requires requires(T& t, const U& u) {
{ t -= u } -> std::same_as<T&>;
}
constexpr cartesian_vector& operator-=(const cartesian_vector<U>& other)
Expand All @@ -225,27 +125,27 @@ class cartesian_vector : public detail::cartesian_vector_iface {
return *this;
}

template<typename U>
requires requires(T t, U u) {
{ t *= u } -> std::same_as<T&>;
template<typename S>
requires requires(T& t, const S& s) {
{ t *= s } -> std::same_as<T&>;
}
constexpr cartesian_vector& operator*=(const U& value)
constexpr cartesian_vector& operator*=(const S& scalar)
{
_coordinates_[0] *= value;
_coordinates_[1] *= value;
_coordinates_[2] *= value;
_coordinates_[0] *= scalar;
_coordinates_[1] *= scalar;
_coordinates_[2] *= scalar;
return *this;
}

template<typename U>
requires requires(T t, U u) {
{ t /= u } -> std::same_as<T&>;
template<typename S>
requires requires(T& t, const S& s) {
{ t /= s } -> std::same_as<T&>;
}
constexpr cartesian_vector& operator/=(const U& value)
constexpr cartesian_vector& operator/=(const S& scalar)
{
_coordinates_[0] /= value;
_coordinates_[1] /= value;
_coordinates_[2] /= value;
_coordinates_[0] /= scalar;
_coordinates_[1] /= scalar;
_coordinates_[2] /= scalar;
return *this;
}

Expand All @@ -261,8 +161,90 @@ class cartesian_vector : public detail::cartesian_vector_iface {
return vec.unit();
}

template<typename U>
requires requires(const T& t, const U& u) { t + u; }
[[nodiscard]] friend constexpr auto operator+(const cartesian_vector& lhs, const cartesian_vector<U>& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] + rhs._coordinates_[0],
lhs._coordinates_[1] + rhs._coordinates_[1],
lhs._coordinates_[2] + rhs._coordinates_[2]};
}

template<typename U>
requires requires(const T& t, const U& u) { t - u; }
[[nodiscard]] friend constexpr auto operator-(const cartesian_vector& lhs, const cartesian_vector<U>& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] - rhs._coordinates_[0],
lhs._coordinates_[1] - rhs._coordinates_[1],
lhs._coordinates_[2] - rhs._coordinates_[2]};
}

template<typename U>
requires(!treat_as_floating_point<T> && !treat_as_floating_point<U> && requires(const T& t, const U& u) { t % u; })
[[nodiscard]] friend constexpr auto operator%(const cartesian_vector& lhs, const cartesian_vector<U>& rhs)
{
using CT = std::common_type_t<T, U>;
return ::mp_units::cartesian_vector<CT>{static_cast<CT>(lhs._coordinates_[0] % rhs._coordinates_[0]),
static_cast<CT>(lhs._coordinates_[1] % rhs._coordinates_[1]),
static_cast<CT>(lhs._coordinates_[2] % rhs._coordinates_[2])};
}

template<typename S>
requires(!std::same_as<std::remove_cvref_t<S>, cartesian_vector>) && detail::NotQuantity<S>
[[nodiscard]] friend constexpr auto operator*(const cartesian_vector& vector, const S& scalar)
{
return ::mp_units::cartesian_vector{vector._coordinates_[0] * scalar, vector._coordinates_[1] * scalar,
vector._coordinates_[2] * scalar};
}

template<typename S>
requires(!std::same_as<std::remove_cvref_t<S>, cartesian_vector>) && detail::NotQuantity<S>
[[nodiscard]] friend constexpr auto operator*(const S& scalar, const cartesian_vector& vector)
{
return vector * scalar;
}

template<typename S>
requires(!std::same_as<std::remove_cvref_t<S>, cartesian_vector>) && detail::NotQuantity<S>
[[nodiscard]] friend constexpr auto operator/(const cartesian_vector& vector, const S& scalar)
{
return ::mp_units::cartesian_vector{vector._coordinates_[0] / scalar, vector._coordinates_[1] / scalar,
vector._coordinates_[2] / scalar};
}

template<std::equality_comparable_with<T> U>
[[nodiscard]] friend constexpr bool operator==(const cartesian_vector& lhs, const cartesian_vector<U>& rhs)
{
return lhs._coordinates_[0] == rhs._coordinates_[0] && lhs._coordinates_[1] == rhs._coordinates_[1] &&
lhs._coordinates_[2] == rhs._coordinates_[2];
}

template<typename U>
requires requires(const T& t, const U& u, decltype(t * u) v) {
t * u;
v + v;
}
[[nodiscard]] friend constexpr auto scalar_product(const cartesian_vector& lhs, const cartesian_vector<U>& rhs)
{
return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] +
lhs._coordinates_[2] * rhs._coordinates_[2];
}

template<typename U>
requires requires(const T& t, const U& u, decltype(t * u) v) {
t * u;
v - v;
}
[[nodiscard]] friend constexpr auto vector_product(const cartesian_vector& lhs, const cartesian_vector<U>& rhs)
{
return ::mp_units::cartesian_vector{
lhs._coordinates_[1] * rhs._coordinates_[2] - lhs._coordinates_[2] * rhs._coordinates_[1],
lhs._coordinates_[2] * rhs._coordinates_[0] - lhs._coordinates_[0] * rhs._coordinates_[2],
lhs._coordinates_[0] * rhs._coordinates_[1] - lhs._coordinates_[1] * rhs._coordinates_[0]};
}

Comment on lines +164 to +245
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of those make the class template operations asymmetrical. RHS argument will not convert, while the LHS will. Please restore my cartesian_vector_iface to fix it -> both arguments will not convert. If you would like to enable conversion for the arguments, it is doable, but requires even more overloads.

#if MP_UNITS_HOSTED
friend constexpr std::ostream& operator<<(std::ostream& os, const cartesian_vector& vec)
friend std::ostream& operator<<(std::ostream& os, const cartesian_vector& vec)
{
return os << '[' << vec[0] << ", " << vec[1] << ", " << vec[2] << ']';
}
Expand All @@ -273,8 +255,30 @@ template<typename Arg, typename... Args>
requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t<Arg, Args...>; }
cartesian_vector(Arg, Args...) -> cartesian_vector<std::common_type_t<Arg, Args...>>;

template<class T>
inline constexpr bool treat_as_floating_point<cartesian_vector<T>> = treat_as_floating_point<T>;

template<class S, class T>
inline constexpr bool is_value_preserving<S, cartesian_vector<T>> = is_value_preserving<S, T>;
Comment on lines +258 to +262
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} // namespace mp_units

namespace std {
template<class T, class U>
struct common_type<mp_units::cartesian_vector<T>, mp_units::cartesian_vector<U>> {
using type = mp_units::cartesian_vector<common_type_t<T, U>>;
};

template<class T, class U>
struct common_type<mp_units::cartesian_vector<T>, U> {
using type = mp_units::cartesian_vector<common_type_t<T, U>>;
};

template<class T, class U>
struct common_type<U, mp_units::cartesian_vector<T>> {
using type = mp_units::cartesian_vector<common_type_t<U, T>>;
};
Comment on lines +265 to +279
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, it seems correct, but why did you have to do it? Was it required by some specific test?

} // namespace std

#if MP_UNITS_HOSTED
// TODO use parse and use formatter for the underlying type
template<typename T, typename Char>
Expand Down
Loading
Loading