diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 79e746b7..65688088 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(unit_tests_static cgs_test.cpp + custom_rep_test.cpp custom_unit_test.cpp data_test.cpp dimension_op_test.cpp diff --git a/test/unit_test/static/custom_rep_test.cpp b/test/unit_test/static/custom_rep_test.cpp new file mode 100644 index 00000000..1909dcb6 --- /dev/null +++ b/test/unit_test/static/custom_rep_test.cpp @@ -0,0 +1,239 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "units/math.h" +#include "units/physical/si/area.h" +#include "units/physical/si/frequency.h" +#include "units/physical/si/velocity.h" +#include +#include + +using namespace units; + +namespace { + +template +struct arithmetic_ops { + // constexpr T& operator+=(T other) { value_ += other.value_; return *this; } + // constexpr T& operator-=(T other) { value_ -= other.value_; return *this; } + // constexpr T& operator*=(T other) { value_ *= other.value_; return *this; } + // constexpr T& operator/=(T other) { value_ /= other.value_; return *this; } + + // [[nodiscard]] constexpr T operator-() const { return T(-value_); } + + [[nodiscard]] friend constexpr T operator+(T lhs, T rhs) { + return T(lhs.value_ + rhs.value_); + } + [[nodiscard]] friend constexpr T operator-(T lhs, T rhs) { + return T(lhs.value_ - rhs.value_); + } + [[nodiscard]] friend constexpr T operator*(T lhs, T rhs) { + return T(lhs.value_ * rhs.value_); + } + [[nodiscard]] friend constexpr T operator/(T lhs, T rhs) { + return T(lhs.value_ / rhs.value_); + } + + [[nodiscard]] friend constexpr bool operator==(T lhs, T rhs) { return lhs.value_ == rhs.value_; } + [[nodiscard]] friend constexpr bool operator!=(T lhs, T rhs) { return !(lhs == rhs); } + [[nodiscard]] friend constexpr bool operator<(T lhs, T rhs) { return lhs.value_ < rhs.value_; } + [[nodiscard]] friend constexpr bool operator>(T lhs, T rhs) { return rhs < lhs; } + [[nodiscard]] friend constexpr bool operator<=(T lhs, T rhs) { return !(rhs < lhs); } + [[nodiscard]] friend constexpr bool operator>=(T lhs, T rhs) { return !(lhs < rhs); } +}; + +template +struct impl_constructible_impl_convertible : arithmetic_ops> { + T value_{}; + impl_constructible_impl_convertible() = default; + constexpr impl_constructible_impl_convertible(T v) : value_(std::move(v)) {} + constexpr operator const T&() const& { return value_; } +}; + +template +using impl_impl = impl_constructible_impl_convertible; + +static_assert(std::convertible_to>); +static_assert(std::convertible_to, float>); +static_assert(units::Scalar>); + +template +struct expl_constructible_impl_convertible : arithmetic_ops> { + T value_{}; + expl_constructible_impl_convertible() = default; + constexpr explicit expl_constructible_impl_convertible(T v) : value_(std::move(v)) {} + constexpr operator const T&() const& { return value_; } +}; + +template +using expl_impl = expl_constructible_impl_convertible; + +static_assert(!std::convertible_to>); +static_assert(std::convertible_to, float>); +static_assert(units::Scalar>); + +template +struct impl_constructible_expl_convertible : arithmetic_ops> { + T value_{}; + impl_constructible_expl_convertible() = default; + constexpr impl_constructible_expl_convertible(T v) : value_(std::move(v)) {} + constexpr explicit operator const T&() const& { return value_; } +}; + +template +using impl_expl = impl_constructible_expl_convertible; + +static_assert(std::convertible_to>); +static_assert(!std::convertible_to, float>); +static_assert(units::Scalar>); + +template +struct expl_constructible_expl_convertible : arithmetic_ops> { + T value_{}; + expl_constructible_expl_convertible() = default; + constexpr explicit expl_constructible_expl_convertible(T v) : value_(std::move(v)) {} + constexpr explicit operator const T&() const& { return value_; } +}; + +template +using expl_expl = expl_constructible_expl_convertible; + +static_assert(!std::convertible_to>); +static_assert(!std::convertible_to, float>); +static_assert(units::Scalar>); + +} // namespace + +namespace units { + +template +inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; + +template +inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; + +template +inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; + +template +inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; + +template +struct quantity_values> { + static constexpr impl_impl zero() { return impl_impl(0); } + static constexpr impl_impl max() { return std::numeric_limits::max(); } + static constexpr impl_impl min() { return std::numeric_limits::lowest(); } +}; + +} // namespace units + +namespace std { + +// template +// struct common_type, my_value> : std::type_identity>> {}; + +// template +// struct common_type, U> : common_type {}; + +// template +// struct common_type> : common_type {}; + +} // namespace std + +namespace { + +using namespace units::si; + +// constructors + +// Quantity from Scalar +// int <- int +static_assert(length(expl_impl(1)).count() == 1); +// static_assert(length(impl_expl(1)).count() == 1); // should not compile (not convertible) +static_assert(length(int(impl_expl(1))).count() == 1); +// static_assert(length>(1).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(length>(expl_impl(1)).count() == expl_impl{1}); +static_assert(length>(1).count() == impl_expl{1}); + +// double <- double +static_assert(length(expl_impl(1.0)).count() == 1.0); +// static_assert(length(impl_expl(1.0)).count() == 1.0); // should not compile (not convertible) +static_assert(length(double(impl_expl(1.0))).count() == 1.0); +// static_assert(length>(1.0).count() == expl_impl{1.0}); // should not compile (not convertible) +static_assert(length>(expl_impl(1.0)).count() == expl_impl{1.0}); +static_assert(length>(1.0).count() == impl_expl{1.0}); + +// double <- int +static_assert(length(expl_impl(1)).count() == 1.0); +// static_assert(length(impl_expl(1)).count() == 1.0); // should not compile (not convertible) +static_assert(length(int(impl_expl(1))).count() == 1.0); +// static_assert(length>(1).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(length>(expl_impl(1)).count() == expl_impl{1}); +static_assert(length>(1).count() == impl_expl{1.0}); + +// int <- double +// static_assert(length(expl_impl(1.0)).count() == 1); // should not compile (truncating conversion) +// static_assert(length>(1.0).count() == impl_expl{1}); // should not compile (truncating conversion) + +// Quantity from other Quantity with different Rep +// int <- int +static_assert(length(length>(expl_impl(1))).count() == 1); +// static_assert(length(length>(1)).count() == 1); // should not compile (not convertible) +static_assert(length(quantity_cast(length>(1))).count() == 1); +// static_assert(length>(length(1)).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(length>(quantity_cast>(length(1))).count() == expl_impl{1}); +static_assert(length>(length(1)).count() == impl_expl{1}); + +// double <- double +static_assert(length(length>(expl_impl(1.0))).count() == 1.0); +// static_assert(length(length>(1.0)).count() == 1.0); // should not compile (not convertible) +static_assert(length(quantity_cast(length>(1.0))).count() == 1.0); +// static_assert(length>(length(1.0).count() == expl_impl{1.0}); // should not compile (not convertible) +static_assert(length>(quantity_cast>(length(1.0))).count() == expl_impl{1.0}); +static_assert(length>(length(1.0)).count() == impl_expl{1.0}); + +// double <- int +static_assert(length(length>(expl_impl(1))).count() == 1.0); +// static_assert(length(length>(1)).count() == 1.0); // should not compile (not convertible) +static_assert(length(quantity_cast(length>(1))).count() == 1.0); +// static_assert(length>(length(1)).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(length>(quantity_cast>(length(1))).count() == expl_impl{1}); +static_assert(length>(length(1)).count() == impl_expl{1.0}); + +// int <- double +// static_assert(length(length>(1.0)).count() == 1); // should not compile (truncating conversion) +// static_assert(length>(length(1.0)).count() == impl_expl{1}); // should not compile (truncating conversion) + +// unit conversions + +static_assert(length>(length>(1)).count() == impl_expl(1000)); +// static_assert(length>(length>(2000)).count() == impl_expl(2)); // should not compile (truncating conversion) +static_assert(length>(quantity_cast(length>(2000))).count() == impl_expl(2)); + +static_assert(length>::zero().count() == impl_impl{0}); +static_assert(length>::min().count() == impl_impl{std::numeric_limits::lowest()}); +static_assert(length>::max().count() == impl_impl{std::numeric_limits::max()}); +static_assert(length>::zero().count() == impl_impl{0.0}); +static_assert(length>::min().count() == impl_impl{std::numeric_limits::lowest()}); +static_assert(length>::max().count() == impl_impl{std::numeric_limits::max()}); + +} // namespace diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index da2f6d8a..b74e72ed 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -27,83 +27,8 @@ #include #include -using namespace units; - namespace { -template -class my_value { - T value_{}; - -public: - my_value() = default; - constexpr my_value(T v) : value_(std::move(v)) {} - - // constexpr my_value& operator+=(my_value other) { value_ += other.value_; return *this; } - // constexpr my_value& operator-=(my_value other) { value_ -= other.value_; return *this; } - // constexpr my_value& operator*=(my_value other) { value_ *= other.value_; return *this; } - // constexpr my_value& operator/=(my_value other) { value_ /= other.value_; return *this; } - - [[nodiscard]] constexpr my_value operator-() const { return my_value(-value_); } - - [[nodiscard]] friend constexpr my_value operator+(my_value lhs, my_value rhs) { - return my_value(lhs.value_ + rhs.value_); - } - [[nodiscard]] friend constexpr my_value operator-(my_value lhs, my_value rhs) { - return my_value(lhs.value_ - rhs.value_); - } - [[nodiscard]] friend constexpr my_value operator*(my_value lhs, my_value rhs) { - return my_value(lhs.value_ * rhs.value_); - } - [[nodiscard]] friend constexpr my_value operator/(my_value lhs, my_value rhs) { - return my_value(lhs.value_ / rhs.value_); - } - - [[nodiscard]] friend constexpr bool operator==(my_value lhs, my_value rhs) { return lhs.value_ == rhs.value_; } - [[nodiscard]] friend constexpr bool operator!=(my_value lhs, my_value rhs) { return !(lhs == rhs); } - [[nodiscard]] friend constexpr bool operator<(my_value lhs, my_value rhs) { return lhs.value_ < rhs.value_; } - [[nodiscard]] friend constexpr bool operator>(my_value lhs, my_value rhs) { return rhs < lhs; } - [[nodiscard]] friend constexpr bool operator<=(my_value lhs, my_value rhs) { return !(rhs < lhs); } - [[nodiscard]] friend constexpr bool operator>=(my_value lhs, my_value rhs) { return !(lhs < rhs); } - - constexpr operator const T&() const& { return value_; } -}; - -} // namespace - -namespace units { - -template -inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; - -template -struct quantity_values> { - static constexpr my_value zero() { return my_value(0); } - static constexpr my_value max() { return std::numeric_limits::max(); } - static constexpr my_value min() { return std::numeric_limits::lowest(); } -}; - -} // namespace units - -namespace std { - -template -struct common_type, my_value> : std::type_identity>> {}; - -template -struct common_type, U> : common_type {}; - -template -struct common_type> : common_type {}; - -} // namespace std - -namespace { - -static_assert(units::Scalar>); -static_assert(std::convertible_to, float>); -static_assert(std::convertible_to>); - using namespace units; using namespace units::si; @@ -123,44 +48,24 @@ static_assert(std::is_same_v::unit, kilometre>); // constructors -using my_int = my_value; -using my_double = my_value; - static_assert(length().count() == 0); constexpr length km{1000}; static_assert(km.count() == 1000); static_assert(length(km).count() == km.count()); static_assert(length(1).count() == 1); -static_assert(length(my_value(1)).count() == 1); -static_assert(length(1).count() == my_int{1}); // static_assert(length(1.0).count() == 1); // should not compile (truncating conversion) -// static_assert(length(my_value(1.0)).count() == 1); // should not compile (truncating conversion) -// static_assert(length(1.0).count() == my_int{1}); // should not compile (truncating conversion) static_assert(length(1.0).count() == 1.0); -static_assert(length(my_value(1.0)).count() == 1.0); static_assert(length(1).count() == 1.0); -static_assert(length(my_value(1)).count() == 1.0); static_assert(length(3.14).count() == 3.14); -static_assert(length(1.0).count() == my_double{1.0}); -static_assert(length(1).count() == my_double{1.0}); -static_assert(length(3.14).count() == my_double{3.14}); static_assert(length(km).count() == 1000); // static_assert(length(length(3.14)).count() == 3); // should not compile (truncating conversion) -static_assert(length(quantity_cast>(3.14q_m)).count() == 3); -// static_assert(length(length(1000.0)).count() == 1000); // should not compile (truncating conversion) -// static_assert(length(1000.0q_m).count() == my_int{1000}); // should not compile (truncating conversion) static_assert(length(1000.0q_m).count() == 1000.0); -static_assert(length(length(1000.0)).count() == 1000.0); -static_assert(length(1000.0q_m).count() == my_double{1000.0}); static_assert(length(km).count() == 1000.0); -static_assert(length(km).count() == my_double{1000.0}); static_assert(length(1q_km).count() == 1000); // static_assert(length(1q_s).count() == 1); // should not compile (different dimensions) //static_assert(length(1010q_m).count() == 1); // should not compile (truncating conversion) -static_assert(length(quantity_cast>(1010q_m)).count() == 1); -static_assert(length(quantity_cast>(1010q_m)).count() == 1000); // assignment operator @@ -174,12 +79,6 @@ static_assert(length::max().count() == std::numeric_limits::max static_assert(length::zero().count() == 0.0); static_assert(length::min().count() == std::numeric_limits::lowest()); static_assert(length::max().count() == std::numeric_limits::max()); -static_assert(length::zero().count() == my_int{0}); -static_assert(length::min().count() == my_int{std::numeric_limits::lowest()}); -static_assert(length::max().count() == my_int{std::numeric_limits::max()}); -static_assert(length::zero().count() == my_double{0.0}); -static_assert(length::min().count() == my_double{std::numeric_limits::lowest()}); -static_assert(length::max().count() == my_double{std::numeric_limits::max()}); // unary member operators