diff --git a/example/v2_framework.cpp b/example/v2_framework.cpp index ffd221d0..adb38118 100644 --- a/example/v2_framework.cpp +++ b/example/v2_framework.cpp @@ -78,16 +78,16 @@ static_assert(10 * isq::length[m] / (2 * isq::time[s]) - 5 * isq::speed[m / s] = static_assert(5 * isq::speed[m / s] - 10 * isq::length[m] / (2 * isq::time[s]) == 0 * isq::speed[m / s]); static_assert( is_same_v>>{}, int>>); + quantity>{}>{}, int>>); static_assert( is_same_v>>{}, int>>); + quantity>{}>{}, int>>); static_assert( is_same_v>>{}, int>>); + quantity>{}>{}, int>>); static_assert( is_same_v>>{}, int>>); + quantity>{}>{}, int>>); // Named and derived dimensions (different units) static_assert(10 / (2 * isq::time[s]) + 5 * isq::frequency[Hz] == 10 * isq::frequency[Hz]); @@ -95,13 +95,13 @@ static_assert(5 * isq::frequency[Hz] + 10 / (2 * isq::time[s]) == 10 * isq::freq static_assert(10 / (2 * isq::time[s]) - 5 * isq::frequency[Hz] == 0 * isq::frequency[Hz]); static_assert(5 * isq::frequency[Hz] - 10 / (2 * isq::time[s]) == 0 * isq::frequency[Hz]); static_assert(is_same_v{}, int>>); + quantity{}, int>>); static_assert(is_same_v{}, int>>); + quantity{}, int>>); static_assert(is_same_v{}, int>>); + quantity{}, int>>); static_assert(is_same_v{}, int>>); + quantity{}, int>>); // Different named dimensions template @@ -118,8 +118,8 @@ constexpr quantity speed = 120 * isq::length[km] / (2 * isq: // Explicit casts allow changing all or only a part of the type static_assert( std::is_same_v(120 * isq::length[km] / (2 * isq::time[h]))), - quantity)>, - per>>{}, + quantity)>, + per>{}>{}, int>>); auto q3 = quantity_cast(120 * isq::length[km] / (2 * isq::time[h])); auto q4 = quantity_cast(120 * isq::length[km] / (2 * isq::time[h])); diff --git a/src/core/include/units/concepts.h b/src/core/include/units/concepts.h index 32a91ea6..8a7d8e2e 100644 --- a/src/core/include/units/concepts.h +++ b/src/core/include/units/concepts.h @@ -32,16 +32,26 @@ namespace units { -template +template struct reference; +namespace detail { + +template +inline constexpr bool is_specialization_of_reference = false; + +template +inline constexpr bool is_specialization_of_reference> = true; + +} // namespace detail + /** * @brief A concept matching all references in the library. * * Satisfied by all specializations of @c reference. */ template -concept Reference = is_specialization_of; +concept Reference = detail::is_specialization_of_reference; namespace detail { diff --git a/src/core/include/units/customization_points.h b/src/core/include/units/customization_points.h index 54bd9c35..3dcfbacc 100644 --- a/src/core/include/units/customization_points.h +++ b/src/core/include/units/customization_points.h @@ -91,7 +91,7 @@ struct quantity_values { /** * @brief Provides support for external quantity-like types * - * The type trait should provide the following nested type aliases: @c dimension, @c unit, @c rep, + * The type trait should provide the following nested values @c dimension, @c unit and type alias @c rep, * and a static member function @c number(T) that will return the raw value of the quantity. * * Usage example can be found in @c units/chrono.h header file. diff --git a/src/core/include/units/dimension.h b/src/core/include/units/dimension.h index 2afbda77..f2ff1ecb 100644 --- a/src/core/include/units/dimension.h +++ b/src/core/include/units/dimension.h @@ -63,7 +63,7 @@ inline constexpr bool is_valid_unit_for_dimension = false; template concept valid_unit_for_dimension = Unit && Dimension && detail::is_valid_unit_for_dimension; -template +template struct reference; /** @@ -104,13 +104,13 @@ struct base_dimension { #ifdef __cpp_explicit_this_parameter template U> - [[nodiscard]] constexpr reference operator[](this const Self, U) + [[nodiscard]] constexpr auto operator[](this const Self, U) #else template U> - [[nodiscard]] constexpr reference operator[](U) const + [[nodiscard]] constexpr auto operator[](U) const #endif { - return {}; + return reference{}; } }; @@ -213,9 +213,9 @@ template struct derived_dimension : detail::derived_dimension_impl { template requires valid_unit_for_dimension || (sizeof...(Ds) == 0 && convertible(U{}, one)) - [[nodiscard]] constexpr reference operator[](this const Self, U) + [[nodiscard]] constexpr auto operator[](this const Self, U) { - return {}; + return reference{}; } }; @@ -228,9 +228,9 @@ template struct derived_dimension : detail::derived_dimension_impl { template requires valid_unit_for_dimension || (sizeof...(Ds) == 0 && convertible(U{}, one)) - [[nodiscard]] constexpr reference operator[](U) const + [[nodiscard]] constexpr auto operator[](U) const { - return {}; + return reference{}; } }; @@ -239,9 +239,9 @@ struct derived_dimension : D { template requires valid_unit_for_dimension || (convertible(derived_dimension{}, derived_dimension<>{}) && convertible(U{}, one)) - [[nodiscard]] constexpr reference operator[](U) const + [[nodiscard]] constexpr auto operator[](U) const { - return {}; + return reference{}; } }; diff --git a/src/core/include/units/quantity.h b/src/core/include/units/quantity.h index d6e9410d..bb2f2209 100644 --- a/src/core/include/units/quantity.h +++ b/src/core/include/units/quantity.h @@ -85,11 +85,11 @@ concept have_quantity_for_ = Quantity && (!Quantity) && quantity_value_for template concept QuantityLike = requires(T q) { - typename quantity_like_traits::dimension; - typename quantity_like_traits::unit; + quantity_like_traits::dimension; + quantity_like_traits::unit; typename quantity_like_traits::rep; - requires Dimension::dimension>; - requires Unit::unit>; + requires Dimension::dimension)>>; + requires Unit::unit)>>; requires Representation::rep>; { quantity_like_traits::number(q) @@ -97,9 +97,8 @@ concept QuantityLike = requires(T q) { }; template -using quantity_like_type = - quantity::dimension, typename quantity_like_traits::unit>{}, - typename quantity_like_traits::rep>; +using quantity_like_type = quantity::dimension, quantity_like_traits::unit>{}, + typename quantity_like_traits::rep>; /** * @brief A quantity @@ -352,42 +351,42 @@ public: [[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const Value& rhs) requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; - requires is_same_v; + requires unit == ::units::one; requires invoke_result_convertible_to_, rep, Value>; } { - return units::quantity(lhs.number() + rhs); + return ::units::quantity(lhs.number() + rhs); } template [[nodiscard]] friend constexpr Quantity auto operator+(const Value& lhs, const quantity& rhs) requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; - requires is_same_v; + requires unit == ::units::one; requires invoke_result_convertible_to_, Value, rep>; } { - return units::quantity(lhs + rhs.number()); + return ::units::quantity(lhs + rhs.number()); } template [[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const Value& rhs) requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; - requires is_same_v; + requires unit == ::units::one; requires invoke_result_convertible_to_, rep, Value>; } { - return units::quantity(lhs.number() - rhs); + return ::units::quantity(lhs.number() - rhs); } template [[nodiscard]] friend constexpr Quantity auto operator-(const Value& lhs, const quantity& rhs) requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; - requires is_same_v; + requires unit == ::units::one; requires invoke_result_convertible_to_, Value, rep>; } { - return units::quantity(lhs - rhs.number()); + return ::units::quantity(lhs - rhs.number()); } template @@ -466,13 +465,13 @@ template explicit(false) quantity(Rep)->quantity; template -explicit quantity(Q) - -> quantity::dimension, typename quantity_like_traits::unit>{}, - typename quantity_like_traits::rep>; +explicit quantity(Q) -> quantity::dimension, quantity_like_traits::unit>{}, + typename quantity_like_traits::rep>; // non-member binary operators -template Q2> - requires(quantity_value_for_, typename Q1::rep, typename Q2::rep>) +template + requires(convertible(Q1::reference, Q2::reference)) && + quantity_value_for_, typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs) { using ref = std::common_type_t; @@ -480,8 +479,9 @@ template Q2> return ret(ret(lhs).number() + ret(rhs).number()); } -template Q2> - requires(quantity_value_for_, typename Q1::rep, typename Q2::rep>) +template + requires(convertible(Q1::reference, Q2::reference)) && + quantity_value_for_, typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs) { using ref = std::common_type_t; @@ -506,26 +506,28 @@ template template requires(!floating_point_) && (!floating_point_) && - (std::convertible_to || quantity_of) && - (quantity_value_for_, typename Q1::rep, typename Q2::rep>) + (convertible(Q1::reference, Q2::reference) || quantity_of) && + quantity_value_for_, typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - using ret = quantity{}, + using ret = quantity{}, std::invoke_result_t, typename Q1::rep, typename Q2::rep>>; return ret(lhs.number() % rhs.number()); } -template Q2> - requires std::three_way_comparable_with +template + requires(convertible(Q1::reference, Q2::reference)) && + std::three_way_comparable_with [[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs) { using ref = std::common_type_t; - return quantity_cast(lhs).number() <=> quantity_cast(rhs).number(); + return quantity_cast(lhs).number() <=> quantity_cast(rhs).number(); } -template Q2> - requires std::equality_comparable_with +template + requires(convertible(Q1::reference, Q2::reference)) && + std::equality_comparable_with [[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs) { using ref = std::common_type_t; diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index c1c835fe..ee98058d 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -145,7 +145,7 @@ template requires(convertible(ToD, R.dimension)) [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - constexpr reference, typename quantity::unit_t> r; + constexpr reference::unit> r; return quantity_cast>(q); } @@ -165,7 +165,7 @@ template requires(convertible(ToU, R.unit)) [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - constexpr reference::dimension_t, std::remove_const_t> r; + constexpr reference::dimension, ToU> r; return quantity_cast>(q); } diff --git a/src/core/include/units/reference.h b/src/core/include/units/reference.h index 5e6debd5..cc312c47 100644 --- a/src/core/include/units/reference.h +++ b/src/core/include/units/reference.h @@ -67,31 +67,28 @@ namespace units { * The following syntaxes are not allowed: * `2 / s`, `km * 3`, `s / 4`, `70 * km / h`. */ -template +template struct reference { - static constexpr D dimension{}; - static constexpr U unit{}; - // static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag; + static constexpr auto dimension = D; + static constexpr auto unit = U; }; // Reference template -[[nodiscard]] consteval reference operator*(M, R) +[[nodiscard]] consteval reference operator*(M, R) { return {}; } template -[[nodiscard]] consteval reference operator*(R1, - R2) +[[nodiscard]] consteval reference operator*(R1, R2) { return {}; } template -[[nodiscard]] consteval reference operator/(R1, - R2) +[[nodiscard]] consteval reference operator/(R1, R2) { return {}; } @@ -119,7 +116,7 @@ struct system_reference { template requires(convertible(coherent_unit, U{})) - [[nodiscard]] constexpr reference, U> operator[](U) const + [[nodiscard]] constexpr reference operator[](U) const { return {}; } @@ -139,7 +136,7 @@ private: using dim = common_type_t, remove_const_t>; using unit = common_type_t, remove_const_t>; public: - using type = units::reference; + using type = units::reference; }; } // namespace std diff --git a/test/unit_test/static/reference_test.cpp b/test/unit_test/static/reference_test.cpp index 91af182b..63940a52 100644 --- a/test/unit_test/static/reference_test.cpp +++ b/test/unit_test/static/reference_test.cpp @@ -91,24 +91,25 @@ inline constexpr struct kilometre_ : decltype(si::kilo) {} kilometre; // clang-format on // Named quantity/dimension and unit -static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); // Named quantity/dimension and derived (unnamed) unit static_assert(is_same_v>>{}, int>>); + quantity>{}>{}, int>>); // Derived (unnamed) quantity/dimension and derived (unnamed) unit static_assert( - is_same_v>, derived_unit>>{}, int>>); + is_same_v< + decltype(10 * length[metre] / (2 * time[second])), + quantity>{}, derived_unit>{}>{}, int>>); // Base quantity as a result of dimensional transformation static_assert( - is_same_v{}, int>>); + is_same_v{}, int>>); // dimension_one static_assert(is_same_v{}, int>>); + quantity{}, int>>); template concept invalid_operations = requires { @@ -139,53 +140,58 @@ concept invalid_operations = requires { static_assert(invalid_operations); static_assert( - is_same_v>, derived_unit>>{}, int>>); + is_same_v< + decltype(2 * length[metre] / (1 * time[second])), + quantity>{}, derived_unit>{}>{}, int>>); static_assert( - is_same_v>, derived_unit>>{}, int>>); + is_same_v< + decltype(2 * (length[metre] / time[second])), + quantity>{}, derived_unit>{}>{}, int>>); static_assert(is_same_v>>{}, int>>); + quantity>{}>{}, int>>); constexpr auto m_per_s = speed[metre / second]; -static_assert(is_same_v>>{}, int>>); +static_assert( + is_same_v>{}>{}, int>>); static_assert( - is_same_v>, derived_unit>>{}, int>>); + is_same_v< + decltype(120 * length[kilometre] / (2 * time[hour])), + quantity>{}, derived_unit>{}>{}, int>>); static_assert(120 * length[kilometre] / (2 * time[hour]) == 60 * speed[kilometre / hour]); -static_assert( - is_same_v>, derived_unit>>{}, int>>); static_assert( is_same_v< - decltype(std::int64_t{120} * length[kilometre] / (2 * time[hour])), - quantity>, derived_unit>>{}, std::int64_t>>); + decltype([] { + const auto distance = 120; + const auto duration = 2; + return distance * length[kilometre] / (duration * time[hour]); + }()), + quantity>{}, derived_unit>{}>{}, int>>); static_assert( - is_same_v< - decltype(120.L * length[kilometre] / (2 * time[hour])), - quantity>, derived_unit>>{}, long double>>); + is_same_v>{}, derived_unit>{}>{}, + std::int64_t>>); +static_assert( + is_same_v>{}, derived_unit>{}>{}, + long double>>); static_assert(is_same_v]), decltype(1. * area[square] / 4)>); static_assert(1. / 4 * area[square] == 1. * area[square] / 4); // Natural Units -static_assert(is_same_v{}, int>>); -static_assert(is_same_v{}, int>>); -static_assert(is_same_v{}, int>>); -static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); static_assert(is_same_v>, one_>{}, int>>); + quantity>{}, one>{}, int>>); static_assert(is_same_v>, one_>{}, int>>); -static_assert(is_same_v{}, int>>); -static_assert(is_same_v{}, int>>); + quantity>{}, one>{}, int>>); +static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); static_assert(is_same_v>, kilogram_>{}, int>>); + quantity>{}, kilogram>{}, int>>); template concept invalid_nu_unit = !requires { dim[unit]; };