diff --git a/src/core/include/mp-units/cartesian_vector.h b/src/core/include/mp-units/cartesian_vector.h index cbc9cda0f4..900b2f7094 100644 --- a/src/core/include/mp-units/cartesian_vector.h +++ b/src/core/include/mp-units/cartesian_vector.h @@ -27,6 +27,7 @@ #include #include #include +#include #if MP_UNITS_HOSTED #include @@ -37,8 +38,10 @@ import std; #else #include +#include #include #include +#include #if MP_UNITS_HOSTED #include #endif @@ -50,104 +53,18 @@ namespace mp_units { MP_UNITS_EXPORT template class cartesian_vector; -namespace detail { - -struct cartesian_vector_iface { - template - requires requires(T t, U u) { t + u; } - [[nodiscard]] friend constexpr auto operator+(const cartesian_vector& lhs, const cartesian_vector& 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 - requires requires(T t, U u) { t - u; } - [[nodiscard]] friend constexpr auto operator-(const cartesian_vector& lhs, const cartesian_vector& 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 - requires requires(T t, U u) { t * u; } - [[nodiscard]] friend constexpr auto operator*(const cartesian_vector& lhs, const U& rhs) - { - return ::mp_units::cartesian_vector{lhs._coordinates_[0] * rhs, lhs._coordinates_[1] * rhs, - lhs._coordinates_[2] * rhs}; - } - - template - requires requires(T t, U u) { t * u; } - [[nodiscard]] friend constexpr auto operator*(const T& lhs, const cartesian_vector& rhs) - { - return rhs * lhs; - } - - template - requires requires(T t, U u) { t / u; } - [[nodiscard]] friend constexpr auto operator/(const cartesian_vector& lhs, const U& rhs) - { - return ::mp_units::cartesian_vector{lhs._coordinates_[0] / rhs, lhs._coordinates_[1] / rhs, - lhs._coordinates_[2] / rhs}; - } - - template U> - [[nodiscard]] friend constexpr bool operator==(const cartesian_vector& lhs, const cartesian_vector& rhs) - { - return lhs._coordinates_[0] == rhs._coordinates_[0] && lhs._coordinates_[1] == rhs._coordinates_[1] && - lhs._coordinates_[2] == rhs._coordinates_[2]; - } - - template - requires requires(T t, U u, decltype(t * u) v) { - t * u; - v + v; - } - [[nodiscard]] friend constexpr auto scalar_product(const cartesian_vector& lhs, const cartesian_vector& rhs) - { - return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] + - lhs._coordinates_[2] * rhs._coordinates_[2]; - } - - template - requires requires(T t, U u, decltype(t * u) v) { - t * u; - v - v; - } - [[nodiscard]] friend constexpr auto vector_product(const cartesian_vector& lhs, const cartesian_vector& 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 -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 - requires(... && std::constructible_from) - constexpr explicit(!(... && std::convertible_to)) cartesian_vector(Args&&... args) : - _coordinates_{static_cast(std::forward(args))...} - { - } + constexpr cartesian_vector() = default; - template - requires std::constructible_from - constexpr explicit(!std::convertible_to) cartesian_vector(const cartesian_vector& other) : - _coordinates_{static_cast(other[0]), static_cast(other[1]), static_cast(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 requires std::constructible_from @@ -157,23 +74,6 @@ class cartesian_vector : public detail::cartesian_vector_iface { { } - template U> - constexpr cartesian_vector& operator=(const cartesian_vector& other) - { - _coordinates_[0] = other[0]; - _coordinates_[1] = other[1]; - _coordinates_[2] = other[2]; - return *this; - } - - template U> - constexpr cartesian_vector& operator=(cartesian_vector&& other) - { - _coordinates_[0] = std::move(other[0]); - _coordinates_[1] = std::move(other[1]); - _coordinates_[2] = std::move(other[2]); - return *this; - } [[nodiscard]] constexpr T magnitude() const requires treat_as_floating_point @@ -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]}; } template - requires requires(T t, U u) { + requires requires(T& t, const U& u) { { t += u } -> std::same_as; } constexpr cartesian_vector& operator+=(const cartesian_vector& other) @@ -214,7 +114,7 @@ class cartesian_vector : public detail::cartesian_vector_iface { } template - requires requires(T t, U u) { + requires requires(T& t, const U& u) { { t -= u } -> std::same_as; } constexpr cartesian_vector& operator-=(const cartesian_vector& other) @@ -225,27 +125,27 @@ class cartesian_vector : public detail::cartesian_vector_iface { return *this; } - template - requires requires(T t, U u) { - { t *= u } -> std::same_as; + template + requires requires(T& t, const S& s) { + { t *= s } -> std::same_as; } - 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 - requires requires(T t, U u) { - { t /= u } -> std::same_as; + template + requires requires(T& t, const S& s) { + { t /= s } -> std::same_as; } - 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; } @@ -261,8 +161,90 @@ class cartesian_vector : public detail::cartesian_vector_iface { return vec.unit(); } + template + requires requires(const T& t, const U& u) { t + u; } + [[nodiscard]] friend constexpr auto operator+(const cartesian_vector& lhs, const cartesian_vector& 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 + requires requires(const T& t, const U& u) { t - u; } + [[nodiscard]] friend constexpr auto operator-(const cartesian_vector& lhs, const cartesian_vector& 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 + requires(!treat_as_floating_point && !treat_as_floating_point && requires(const T& t, const U& u) { t % u; }) + [[nodiscard]] friend constexpr auto operator%(const cartesian_vector& lhs, const cartesian_vector& rhs) + { + using CT = std::common_type_t; + return ::mp_units::cartesian_vector{static_cast(lhs._coordinates_[0] % rhs._coordinates_[0]), + static_cast(lhs._coordinates_[1] % rhs._coordinates_[1]), + static_cast(lhs._coordinates_[2] % rhs._coordinates_[2])}; + } + + template + requires(!std::same_as, cartesian_vector>) && detail::NotQuantity + [[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 + requires(!std::same_as, cartesian_vector>) && detail::NotQuantity + [[nodiscard]] friend constexpr auto operator*(const S& scalar, const cartesian_vector& vector) + { + return vector * scalar; + } + + template + requires(!std::same_as, cartesian_vector>) && detail::NotQuantity + [[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 U> + [[nodiscard]] friend constexpr bool operator==(const cartesian_vector& lhs, const cartesian_vector& rhs) + { + return lhs._coordinates_[0] == rhs._coordinates_[0] && lhs._coordinates_[1] == rhs._coordinates_[1] && + lhs._coordinates_[2] == rhs._coordinates_[2]; + } + + template + 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& rhs) + { + return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] + + lhs._coordinates_[2] * rhs._coordinates_[2]; + } + + template + 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& 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]}; + } + #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] << ']'; } @@ -273,8 +255,30 @@ template requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t; } cartesian_vector(Arg, Args...) -> cartesian_vector>; +template +inline constexpr bool treat_as_floating_point> = treat_as_floating_point; + +template +inline constexpr bool is_value_preserving> = is_value_preserving; } // namespace mp_units +namespace std { +template +struct common_type, mp_units::cartesian_vector> { + using type = mp_units::cartesian_vector>; +}; + +template +struct common_type, U> { + using type = mp_units::cartesian_vector>; +}; + +template +struct common_type> { + using type = mp_units::cartesian_vector>; +}; +} // namespace std + #if MP_UNITS_HOSTED // TODO use parse and use formatter for the underlying type template diff --git a/test/runtime/cartesian_vector_test.cpp b/test/runtime/cartesian_vector_test.cpp index 6aac39f229..dadacf3892 100644 --- a/test/runtime/cartesian_vector_test.cpp +++ b/test/runtime/cartesian_vector_test.cpp @@ -20,11 +20,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "almost_equals.h" #include #include -#include #include + #ifdef MP_UNITS_IMPORT_STD import std; #else @@ -43,14 +42,6 @@ TEST_CASE("cartesian_vector operations", "[vector]") { SECTION("cartesian_vector initialization and access") { - SECTION("no arguments") - { - cartesian_vector v; - REQUIRE(v[0] == 0); - REQUIRE(v[1] == 0); - REQUIRE(v[2] == 0); - } - SECTION("zero arguments") { cartesian_vector v{}; @@ -58,31 +49,27 @@ TEST_CASE("cartesian_vector operations", "[vector]") REQUIRE(v[1] == 0); REQUIRE(v[2] == 0); } - - SECTION("one argument") + SECTION("one argument -> [x,0,0]") { cartesian_vector v{1.0}; REQUIRE(v[0] == 1.0); - REQUIRE(v[1] == 0); - REQUIRE(v[2] == 0); + REQUIRE(v[1] == 0.0); + REQUIRE(v[2] == 0.0); } - - SECTION("two arguments") + SECTION("two arguments -> [x,y,0]") { cartesian_vector v{1.0, 2.0}; REQUIRE(v[0] == 1.0); REQUIRE(v[1] == 2.0); - REQUIRE(v[2] == 0); + REQUIRE(v[2] == 0.0); } - - SECTION("all arguments") + SECTION("three arguments") { cartesian_vector v{1.0, 2.0, 3.0}; REQUIRE(v[0] == 1.0); REQUIRE(v[1] == 2.0); REQUIRE(v[2] == 3.0); } - SECTION("convertible arguments") { cartesian_vector v{1, 2, 3}; @@ -94,7 +81,7 @@ TEST_CASE("cartesian_vector operations", "[vector]") SECTION("convertibility from another vector") { - cartesian_vector v1{1, 2, 3}; + cartesian_vector v1{1.0, 2.0, 3.0}; SECTION("construction") { @@ -116,40 +103,40 @@ TEST_CASE("cartesian_vector operations", "[vector]") SECTION("cartesian_vector compound assignment addition") { - cartesian_vector v1{1.0, 2.0, 3.0}; - cartesian_vector v2{4.0, 5.0, 6.0}; - v1 += v2; - REQUIRE(v1[0] == 5.0); - REQUIRE(v1[1] == 7.0); - REQUIRE(v1[2] == 9.0); - } - - SECTION("cartesian_vector compound assignment subtraction") - { - cartesian_vector v1{4.0, 5.0, 6.0}; - cartesian_vector v2{1.0, 2.0, 3.0}; - v1 -= v2; - REQUIRE(v1[0] == 3.0); - REQUIRE(v1[1] == 3.0); - REQUIRE(v1[2] == 3.0); - } - - SECTION("cartesian_vector compound assignment scalar multiplication") - { - cartesian_vector v{1.0, 2.0, 3.0}; - v *= 2.0; - REQUIRE(v[0] == 2.0); - REQUIRE(v[1] == 4.0); - REQUIRE(v[2] == 6.0); - } - - SECTION("cartesian_vector compound assignment scalar division") - { - cartesian_vector v{2.0, 4.0, 6.0}; - v /= 2.0; - REQUIRE(v[0] == 1.0); - REQUIRE(v[1] == 2.0); - REQUIRE(v[2] == 3.0); + SECTION("operator+=") + { + cartesian_vector v1{1.0, 2.0, 3.0}; + cartesian_vector v2{4.0, 5.0, 6.0}; + v1 += v2; + REQUIRE(v1[0] == 5.0); + REQUIRE(v1[1] == 7.0); + REQUIRE(v1[2] == 9.0); + } + SECTION("cartesian_vector compound assignment subtraction") + { + cartesian_vector v1{4.0, 5.0, 6.0}; + cartesian_vector v2{1.0, 2.0, 3.0}; + v1 -= v2; + REQUIRE(v1[0] == 3.0); + REQUIRE(v1[1] == 3.0); + REQUIRE(v1[2] == 3.0); + } + SECTION("cartesian_vector compound assignment scalar multiplication") + { + cartesian_vector v{1.0, 2.0, 3.0}; + v *= 2.0; + REQUIRE(v[0] == 2.0); + REQUIRE(v[1] == 4.0); + REQUIRE(v[2] == 6.0); + } + SECTION("cartesian_vector compound assignment scalar division") + { + cartesian_vector v{2.0, 4.0, 6.0}; + v /= 2.0; + REQUIRE(v[0] == 1.0); + REQUIRE(v[1] == 2.0); + REQUIRE(v[2] == 3.0); + } } SECTION("cartesian_vector addition") @@ -195,7 +182,7 @@ TEST_CASE("cartesian_vector operations", "[vector]") } } - SECTION("cartesian_vector subtraction") + SECTION("cartesian_vector substraction") { SECTION("double - double") { diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index 011330c192..19f7ca8a12 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -305,6 +305,7 @@ static_assert(!std::convertible_to>, cart static_assert(std::constructible_from, quantity>>); static_assert(!std::convertible_to, cartesian_vector>); static_assert(std::constructible_from, quantity>); + #endif static_assert(!std::convertible_to, double>);