diff --git a/src/core/include/mp-units/framework/dimension.h b/src/core/include/mp-units/framework/dimension.h index cbd3609d..47dac3bb 100644 --- a/src/core/include/mp-units/framework/dimension.h +++ b/src/core/include/mp-units/framework/dimension.h @@ -47,6 +47,53 @@ namespace mp_units { +template +struct derived_dimension; + +MP_UNITS_EXPORT struct dimension_one; + +namespace detail { + +template +struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {}; + +template +using type_list_of_base_dimension_less = expr_less; + +template +struct derived_dimension_impl : expr_fractions {}; + +template +[[nodiscard]] consteval std::true_type derived_from_the_same_base_dimension(const base_dimension&, + const base_dimension&) +{ + return {}; +} + +[[nodiscard]] consteval std::false_type derived_from_the_same_base_dimension(...) { return {}; } + +struct dimension_interface { + template + [[nodiscard]] friend consteval Dimension auto operator*(Lhs, Rhs) + { + return expr_multiply(Lhs{}, Rhs{}); + } + + template + [[nodiscard]] friend consteval Dimension auto operator/(Lhs, Rhs) + { + return expr_divide(Lhs{}, Rhs{}); + } + + template + [[nodiscard]] friend consteval bool operator==(Lhs lhs, Rhs rhs) + { + return is_same_v || derived_from_the_same_base_dimension(lhs, rhs); + } +}; + +} // namespace detail + /** * @brief A dimension of a base quantity * @@ -76,23 +123,10 @@ namespace mp_units { * @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support */ MP_UNITS_EXPORT template -struct base_dimension { +struct base_dimension : detail::dimension_interface { static constexpr auto symbol = Symbol; ///< Unique base dimension identifier }; -namespace detail { - -template -struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {}; - -template -using type_list_of_base_dimension_less = expr_less; - -template -struct derived_dimension_impl : detail::expr_fractions {}; - -} // namespace detail - /** * @brief A dimension of a derived quantity * @@ -136,7 +170,7 @@ struct derived_dimension_impl : detail::expr_fractions -struct derived_dimension final : detail::derived_dimension_impl {}; +struct derived_dimension final : detail::dimension_interface, detail::derived_dimension_impl {}; /** * @brief Dimension one @@ -145,7 +179,9 @@ struct derived_dimension final : detail::derived_dimension_impl {}; * dimensions are zero. It is a dimension of a quantity of dimension one also known as * "dimensionless". */ -MP_UNITS_EXPORT inline constexpr struct dimension_one final : detail::derived_dimension_impl<> { +MP_UNITS_EXPORT inline constexpr struct dimension_one final : + detail::dimension_interface, + detail::derived_dimension_impl<> { } dimension_one; namespace detail { @@ -155,43 +191,8 @@ struct is_dimension_one : std::true_type {}; } // namespace detail -// Operators - -MP_UNITS_EXPORT template -[[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs) -{ - return detail::expr_multiply( - Lhs{}, Rhs{}); -} - -MP_UNITS_EXPORT template -[[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs) -{ - return detail::expr_divide(Lhs{}, - Rhs{}); -} - -namespace detail { - -template -[[nodiscard]] consteval std::true_type derived_from_the_same_base_dimension(const base_dimension&, - const base_dimension&) -{ - return {}; -} - -[[nodiscard]] consteval std::false_type derived_from_the_same_base_dimension(...) { return {}; } - -} // namespace detail - MP_UNITS_EXPORT_BEGIN -template -[[nodiscard]] consteval bool operator==(Lhs lhs, Rhs rhs) -{ - return is_same_v || detail::derived_from_the_same_base_dimension(lhs, rhs); -} - [[nodiscard]] consteval Dimension auto inverse(Dimension auto d) { return dimension_one / d; } /** diff --git a/src/core/include/mp-units/framework/dimension_concepts.h b/src/core/include/mp-units/framework/dimension_concepts.h index c0a31ce0..7517ea7f 100644 --- a/src/core/include/mp-units/framework/dimension_concepts.h +++ b/src/core/include/mp-units/framework/dimension_concepts.h @@ -30,6 +30,20 @@ namespace mp_units { +namespace detail { + +struct dimension_interface; + +} + +/** + * @brief A concept matching all dimensions in the library. + * + * Satisfied by all dimension types in the library. + */ +MP_UNITS_EXPORT template +concept Dimension = std::derived_from && std::is_final_v; + MP_UNITS_EXPORT template struct base_dimension; @@ -48,7 +62,7 @@ inline constexpr bool is_derived_from_specialization_of_base_dimension = * Satisfied by all dimension types derived from a specialization of `base_dimension`. */ template -concept BaseDimension = is_derived_from_specialization_of_base_dimension && std::is_final_v; +concept BaseDimension = Dimension && is_derived_from_specialization_of_base_dimension; template struct is_dimension_one : std::false_type {}; @@ -70,40 +84,11 @@ template concept DerivedDimensionExpr = BaseDimension || is_dimension_one::value || is_power_of_dim || is_per_of_dims; -} // namespace detail - -template -struct derived_dimension; - -namespace detail { - -/** - * @brief A concept matching all derived dimensions in the library. - * - * Satisfied by all dimension types being a specialization of `derived_dimension` or - * being the `dimension_one`. - */ -template -concept DerivedDimension = - (is_specialization_of || is_dimension_one::value) && std::is_final_v; - -} // namespace detail - -/** - * @brief A concept matching all dimensions in the library. - * - * Satisfied by all dimension types for which either `BaseDimension` or `DerivedDimension` is `true`. - */ -MP_UNITS_EXPORT template -concept Dimension = detail::BaseDimension || detail::DerivedDimension; - -namespace detail { - template concept SameDimension = Dimension && Dimension && (D1 == D2); -} +} // namespace detail /** * @brief A concept checking if the argument is of the same dimension. diff --git a/src/core/include/mp-units/framework/magnitude.h b/src/core/include/mp-units/framework/magnitude.h index dde4a87b..dd083de8 100644 --- a/src/core/include/mp-units/framework/magnitude.h +++ b/src/core/include/mp-units/framework/magnitude.h @@ -185,6 +185,9 @@ inline constexpr bool is_specialization_of_power_v> = true; template concept MagnitudeSpec = PowerVBase || detail::is_specialization_of_power_v; +template +struct magnitude; + namespace detail { template @@ -464,123 +467,8 @@ namespace detail { return is_rational(element) && get_exponent(element).num > 0; } -} // namespace detail - - -/** - * @brief A representation for positive real numbers which optimizes taking products and rational powers. - * - * Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to - * rational powers, and compare for equality. - */ -template -// requires detail::is_element_pack_valid -struct magnitude { - [[nodiscard]] friend consteval bool is_integral(const magnitude&) - { - using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec - return (is_integral(Ms) && ...); - } - - [[nodiscard]] friend consteval bool is_rational(const magnitude&) - { - using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec - return (is_rational(Ms) && ...); - } -}; - - -namespace detail { - -template -void to_base_specialization_of_magnitude(const volatile magnitude*); - -template -inline constexpr bool is_derived_from_specialization_of_magnitude = - requires(T* t) { to_base_specialization_of_magnitude(t); }; - -template - requires is_derived_from_specialization_of_magnitude -inline constexpr bool is_magnitude = true; - -template -inline constexpr bool is_specialization_of_magnitude> = true; - -} // namespace detail - - -/** - * @brief The value of a Magnitude in a desired type T. - */ -template - requires(is_integral(magnitude{})) || treat_as_floating_point -[[nodiscard]] consteval T get_value(const magnitude&) -{ - // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow. - constexpr T result = detail::checked_static_cast((detail::compute_base_power(Ms) * ... * T{1})); - return result; -} - -MP_UNITS_EXPORT_BEGIN - -/** - * @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number. - */ -#if MP_UNITS_COMP_CLANG - -inline constexpr struct mag_pi : magnitude}> { -} mag_pi; - -#else - -inline constexpr struct mag_pi : magnitude> { -} mag_pi; - -#endif - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Magnitude equality implementation. - -template -[[nodiscard]] consteval bool operator==(M1, M2) -{ - return std::is_same_v; -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Magnitude rational powers implementation. - -template -[[nodiscard]] consteval auto pow(magnitude) -{ - if constexpr (Num == 0) { - return magnitude<>{}; - } else { - return magnitude< - detail::power_v_or_T()...>{}; - } -} - -template -[[nodiscard]] consteval auto sqrt(magnitude m) -{ - return pow<1, 2>(m); -} - -template -[[nodiscard]] consteval auto cbrt(magnitude m) -{ - return pow<1, 3>(m); -} - -MP_UNITS_EXPORT_END - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude product implementation. - -namespace detail { - [[nodiscard]] consteval bool less(MagnitudeSpec auto lhs, MagnitudeSpec auto rhs) { using lhs_base_t = decltype(get_base_value(lhs)); @@ -594,18 +482,8 @@ namespace detail { return is_named_magnitude; } -} // namespace detail - -MP_UNITS_EXPORT_BEGIN - -// Base cases, for when either (or both) inputs are the identity. -[[nodiscard]] consteval Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; } -[[nodiscard]] consteval Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; } -[[nodiscard]] consteval Magnitude auto operator*(Magnitude auto m, magnitude<>) { return m; } - -// Recursive case for the product of any two non-identity Magnitudes. template -[[nodiscard]] consteval Magnitude auto operator*(magnitude, magnitude) +[[nodiscard]] consteval Magnitude auto multiply_impl(magnitude, magnitude) { using namespace detail; @@ -641,10 +519,123 @@ template } } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Magnitude quotient implementation. +} // namespace detail -[[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); } + +/** + * @brief A representation for positive real numbers which optimizes taking products and rational powers. + * + * Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to + * rational powers, and compare for equality. + */ +template +// requires detail::is_element_pack_valid +struct magnitude { + [[nodiscard]] friend consteval bool is_integral(const magnitude&) + { + using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec + return (is_integral(Ms) && ...); + } + + [[nodiscard]] friend consteval bool is_rational(const magnitude&) + { + using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec + return (is_rational(Ms) && ...); + } + + template + [[nodiscard]] friend consteval Magnitude auto operator*(magnitude m1, M m2) + { + if constexpr (sizeof...(Ms) == 0) + return m2; + else if constexpr (is_same_v>) + return m1; + else + return detail::multiply_impl(m1, m2); + } + + [[nodiscard]] friend consteval auto operator/(magnitude l, Magnitude auto r) { return l * pow<-1>(r); } + + template + [[nodiscard]] friend consteval bool operator==(magnitude, M2) + { + return std::is_same_v; + } + + /** + * @brief The value of a Magnitude in a desired type T. + */ + template + requires((detail::is_integral(Ms) && ...)) || treat_as_floating_point + [[nodiscard]] friend consteval T get_value(const magnitude&) + { + // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow. + constexpr T result = detail::checked_static_cast((detail::compute_base_power(Ms) * ... * T{1})); + return result; + } +}; + + +namespace detail { + +template +void to_base_specialization_of_magnitude(const volatile magnitude*); + +template +inline constexpr bool is_derived_from_specialization_of_magnitude = + requires(T* t) { to_base_specialization_of_magnitude(t); }; + +template + requires is_derived_from_specialization_of_magnitude +inline constexpr bool is_magnitude = true; + +template +inline constexpr bool is_specialization_of_magnitude> = true; + +} // namespace detail + +MP_UNITS_EXPORT_BEGIN + +/** + * @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number. + */ +#if MP_UNITS_COMP_CLANG + +inline constexpr struct mag_pi : magnitude}> { +} mag_pi; + +#else + +inline constexpr struct mag_pi : magnitude> { +} mag_pi; + +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Magnitude rational powers implementation. + +template +[[nodiscard]] consteval auto pow(magnitude) +{ + if constexpr (Num == 0) { + return magnitude<>{}; + } else { + return magnitude< + detail::power_v_or_T()...>{}; + } +} + +template +[[nodiscard]] consteval auto sqrt(magnitude m) +{ + return pow<1, 2>(m); +} + +template +[[nodiscard]] consteval auto cbrt(magnitude m) +{ + return pow<1, 3>(m); +} MP_UNITS_EXPORT_END diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index d9395df3..5380366b 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -36,13 +36,81 @@ namespace mp_units { +namespace detail { + +template +inline constexpr bool is_specialization_of_zeroth_point_origin = false; + +template +[[nodiscard]] consteval bool is_zeroth_point_origin(PO) +{ + return is_specialization_of_zeroth_point_origin; +} + +struct point_origin_interface { + template + requires ReferenceOf, PO::quantity_spec> + [[nodiscard]] friend constexpr quantity_point operator+(PO, Q&& q) + { + return quantity_point{std::forward(q), PO{}}; + } + + template + requires ReferenceOf, PO::quantity_spec> + [[nodiscard]] friend constexpr quantity_point operator+(Q&& q, PO po) + { + return po + std::forward(q); + } + + template + requires ReferenceOf, PO::quantity_spec> + [[nodiscard]] friend constexpr QuantityPoint auto operator-(PO po, const Q& q) + requires requires { -q; } + { + return po + (-q); + } + + template PO2> + requires QuantitySpecOf, PO2::quantity_spec> && + (detail::is_derived_from_specialization_of_relative_point_origin || + detail::is_derived_from_specialization_of_relative_point_origin) + [[nodiscard]] friend constexpr Quantity auto operator-(PO1 po1, PO2 po2) + { + if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + return -(po2.quantity_point - po2.quantity_point.absolute_point_origin); + } else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + return po1.quantity_point - po1.quantity_point.absolute_point_origin; + } else { + return po1.quantity_point - po2.quantity_point; + } + } + + template + [[nodiscard]] friend consteval bool operator==(PO1 po1, PO2 po2) + { + if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin && + detail::is_derived_from_specialization_of_absolute_point_origin) + return is_same_v || (detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) && + interconvertible(po1.quantity_spec, po2.quantity_spec)); + else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin && + detail::is_derived_from_specialization_of_relative_point_origin) + return PO1::quantity_point == PO2::quantity_point; + else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin) + return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO1::quantity_point.quantity_from_zero()); + else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin) + return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO2::quantity_point.quantity_from_zero()); + } +}; + +} // namespace detail + MP_UNITS_EXPORT template -struct absolute_point_origin { +struct absolute_point_origin : detail::point_origin_interface { static constexpr QuantitySpec auto quantity_spec = QS; }; MP_UNITS_EXPORT template -struct relative_point_origin { +struct relative_point_origin : detail::point_origin_interface { static constexpr QuantityPoint auto quantity_point = QP; static constexpr QuantitySpec auto quantity_spec = []() { // select the strongest of specs @@ -62,33 +130,11 @@ inline constexpr zeroth_point_origin_ zeroth_point_origin; namespace detail { -template -inline constexpr bool is_specialization_of_zeroth_point_origin = false; - template inline constexpr bool is_specialization_of_zeroth_point_origin> = true; -template -[[nodiscard]] consteval bool is_zeroth_point_origin(PO) -{ - return is_specialization_of_zeroth_point_origin; -} - } // namespace detail -MP_UNITS_EXPORT template -[[nodiscard]] consteval bool operator==(PO1 po1, PO2 po2) -{ - if constexpr (detail::AbsolutePointOrigin && detail::AbsolutePointOrigin) - return is_same_v || (detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) && - interconvertible(po1.quantity_spec, po2.quantity_spec)); - else if constexpr (detail::RelativePointOrigin && detail::RelativePointOrigin) - return PO1::quantity_point == PO2::quantity_point; - else if constexpr (detail::RelativePointOrigin) - return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO1::quantity_point.quantity_from_zero()); - else if constexpr (detail::RelativePointOrigin) - return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO2::quantity_point.quantity_from_zero()); -} MP_UNITS_EXPORT template [[nodiscard]] consteval PointOriginFor auto default_point_origin(R) @@ -104,7 +150,7 @@ namespace detail { template [[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PO po) { - if constexpr (AbsolutePointOrigin) + if constexpr (is_derived_from_specialization_of_absolute_point_origin) return po; else return po.quantity_point.absolute_point_origin; @@ -112,8 +158,6 @@ template } // namespace detail -MP_UNITS_EXPORT_BEGIN - /** * @brief A quantity point * @@ -123,8 +167,8 @@ MP_UNITS_EXPORT_BEGIN * @tparam PO a type that represents the origin point from which the quantity point is measured from * @tparam Rep a type to be used to represent values of a quantity point */ -template auto PO = default_point_origin(R), - RepresentationOf Rep = double> +MP_UNITS_EXPORT template auto PO = default_point_origin(R), + RepresentationOf Rep = double> class quantity_point { public: // member types and values @@ -524,44 +568,4 @@ explicit(is_specialization_of::to_numeri -> quantity_point::reference, quantity_point_like_traits::point_origin, typename quantity_point_like_traits::rep>; -// binary operators on point origins -template - requires ReferenceOf, PO::quantity_spec> -[[nodiscard]] constexpr quantity_point operator+(PO, Q&& q) -{ - return quantity_point{std::forward(q), PO{}}; -} - -template - requires ReferenceOf, PO::quantity_spec> -[[nodiscard]] constexpr quantity_point operator+(Q&& q, PO po) -{ - return po + std::forward(q); -} - -template - requires ReferenceOf, PO::quantity_spec> -[[nodiscard]] constexpr QuantityPoint auto operator-(PO po, const Q& q) - requires requires { -q; } -{ - return po + (-q); -} - -template PO2> - requires QuantitySpecOf, PO2::quantity_spec> && - (detail::is_derived_from_specialization_of_relative_point_origin || - detail::is_derived_from_specialization_of_relative_point_origin) -[[nodiscard]] constexpr Quantity auto operator-(PO1 po1, PO2 po2) -{ - if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { - return -(po2.quantity_point - po2.quantity_point.absolute_point_origin); - } else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { - return po1.quantity_point - po1.quantity_point.absolute_point_origin; - } else { - return po1.quantity_point - po2.quantity_point; - } -} - -MP_UNITS_EXPORT_END - } // namespace mp_units diff --git a/src/core/include/mp-units/framework/quantity_point_concepts.h b/src/core/include/mp-units/framework/quantity_point_concepts.h index 212ff75d..a262ea7d 100644 --- a/src/core/include/mp-units/framework/quantity_point_concepts.h +++ b/src/core/include/mp-units/framework/quantity_point_concepts.h @@ -47,9 +47,6 @@ template inline constexpr bool is_derived_from_specialization_of_absolute_point_origin = requires(T* t) { to_base_specialization_of_absolute_point_origin(t); }; -template -concept AbsolutePointOrigin = is_derived_from_specialization_of_absolute_point_origin && std::is_final_v; - } // namespace detail /** @@ -72,8 +69,7 @@ template inline constexpr bool is_derived_from_specialization_of_relative_point_origin = requires(T* t) { to_base_specialization_of_relative_point_origin(t); }; -template -concept RelativePointOrigin = is_derived_from_specialization_of_relative_point_origin && std::is_final_v; +struct point_origin_interface; } // namespace detail @@ -83,7 +79,7 @@ concept RelativePointOrigin = is_derived_from_specialization_of_relative_point_o * Satisfied by either quantity points or by all types derived from `absolute_point_origin` class template. */ MP_UNITS_EXPORT template -concept PointOrigin = detail::AbsolutePointOrigin || detail::RelativePointOrigin; +concept PointOrigin = std::derived_from && std::is_final_v; /** * @brief A concept matching all quantity point origins for a specified quantity type in the library @@ -113,13 +109,15 @@ inline constexpr bool is_quantity_point = true; template [[nodiscard]] consteval bool same_absolute_point_origins(PO1 po1, PO2 po2) { - if constexpr (AbsolutePointOrigin && AbsolutePointOrigin) + if constexpr (is_derived_from_specialization_of_absolute_point_origin && + is_derived_from_specialization_of_absolute_point_origin) return po1 == po2; - else if constexpr (RelativePointOrigin && RelativePointOrigin) + else if constexpr (is_derived_from_specialization_of_relative_point_origin && + is_derived_from_specialization_of_relative_point_origin) return po1.absolute_point_origin == po2.absolute_point_origin; - else if constexpr (RelativePointOrigin) + else if constexpr (is_derived_from_specialization_of_relative_point_origin) return po1.absolute_point_origin == po2; - else if constexpr (RelativePointOrigin) + else if constexpr (is_derived_from_specialization_of_relative_point_origin) return po1 == po2.absolute_point_origin; else return false; diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index c14fe776..c2c7fb8d 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -46,13 +46,15 @@ namespace mp_units { +MP_UNITS_EXPORT struct dimensionless; + namespace detail { template requires(!AssociatedUnit) || UnitOf [[nodiscard]] consteval Reference auto make_reference(QS, U u) { - if constexpr (detail::QuantityKindSpec) + if constexpr (QuantityKindSpec) return u; else return reference{}; @@ -100,7 +102,7 @@ template } template -struct quantity_spec_less : std::bool_constant() < detail::type_name()> {}; +struct quantity_spec_less : std::bool_constant() < type_name()> {}; template using type_list_of_quantity_spec_less = expr_less; @@ -112,6 +114,12 @@ using to_dimension = std::remove_const_t; template [[nodiscard]] consteval auto get_associated_quantity(U); +template +[[nodiscard]] consteval QuantitySpec auto clone_kind_of(Q q); + +template +[[nodiscard]] consteval auto remove_kind(Q q); + #if !MP_UNITS_API_NO_CRTP template #endif @@ -120,33 +128,55 @@ struct quantity_spec_interface { template U> [[nodiscard]] consteval Reference auto operator[](this Self self, U u) { - return detail::make_reference(self, u); + return make_reference(self, u); } template requires Quantity> && - detail::QuantitySpecExplicitlyConvertibleTo::quantity_spec, Self{}> + QuantitySpecExplicitlyConvertibleTo::quantity_spec, Self{}> [[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q) { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - detail::make_reference(self, std::remove_cvref_t::unit)}; + make_reference(self, std::remove_cvref_t::unit)}; } #else template U> [[nodiscard]] MP_UNITS_CONSTEVAL Reference auto operator[](U u) const { - return detail::make_reference(Self{}, u); + return make_reference(Self{}, u); } template requires Quantity> && - detail::QuantitySpecExplicitlyConvertibleTo::quantity_spec, Self_{}> + QuantitySpecExplicitlyConvertibleTo::quantity_spec, Self_{}> [[nodiscard]] constexpr Quantity auto operator()(Q&& q) const { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, - detail::make_reference(Self{}, std::remove_cvref_t::unit)}; + make_reference(Self{}, std::remove_cvref_t::unit)}; } #endif + + template + [[nodiscard]] friend consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs) + { + return clone_kind_of( + expr_multiply(remove_kind(lhs), + remove_kind(rhs))); + } + + template + [[nodiscard]] friend consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs) + { + return clone_kind_of( + expr_divide(remove_kind(lhs), + remove_kind(rhs))); + } + + template + [[nodiscard]] friend consteval bool operator==(Lhs, Rhs) + { + return is_same_v; + } }; } // namespace detail @@ -546,30 +576,6 @@ template MP_UNITS_EXPORT_BEGIN -// Operators - -template -[[nodiscard]] consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs) -{ - return detail::clone_kind_of( - detail::expr_multiply( - detail::remove_kind(lhs), detail::remove_kind(rhs))); -} - -template -[[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs) -{ - return detail::clone_kind_of( - detail::expr_divide( - detail::remove_kind(lhs), detail::remove_kind(rhs))); -} - -template -[[nodiscard]] consteval bool operator==(Lhs, Rhs) -{ - return is_same_v; -} - [[nodiscard]] consteval QuantitySpec auto inverse(QuantitySpec auto q) { return dimensionless / q; } diff --git a/src/core/include/mp-units/framework/quantity_spec_concepts.h b/src/core/include/mp-units/framework/quantity_spec_concepts.h index f7d9184b..af939c59 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -30,6 +30,31 @@ namespace mp_units { +#if MP_UNITS_API_NO_CRTP + +namespace detail { + +struct quantity_spec_interface; + +} + +MP_UNITS_EXPORT template +concept QuantitySpec = std::derived_from && std::is_final_v; + +#else + +namespace detail { + +template +struct quantity_spec_interface; + +} + +MP_UNITS_EXPORT template +concept QuantitySpec = is_derived_from_specialization_of && std::is_final_v; + +#endif + MP_UNITS_EXPORT #if MP_UNITS_API_NO_CRTP template @@ -71,7 +96,7 @@ inline constexpr bool is_derived_from_specialization_of_quantity_spec = */ template concept NamedQuantitySpec = - is_derived_from_specialization_of_quantity_spec && std::is_final_v && (!QuantityKindSpec); + QuantitySpec && is_derived_from_specialization_of_quantity_spec && (!QuantityKindSpec); template struct is_dimensionless : std::false_type {}; @@ -110,16 +135,13 @@ namespace detail { */ template concept DerivedQuantitySpec = - is_specialization_of || - (QuantityKindSpec && - is_specialization_of, derived_quantity_spec>); + QuantitySpec && (is_specialization_of || + (QuantityKindSpec && + is_specialization_of, derived_quantity_spec>)); } // namespace detail -MP_UNITS_EXPORT template -concept QuantitySpec = detail::NamedQuantitySpec || detail::DerivedQuantitySpec || detail::QuantityKindSpec; - MP_UNITS_EXPORT template [[nodiscard]] consteval detail::QuantityKindSpec auto get_kind(Q q); diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index f32f4b40..56e7182d 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -56,6 +56,155 @@ namespace mp_units { namespace detail { +template +struct scaled_unit_impl; + +template +inline constexpr bool is_specialization_of_scaled_unit = false; + +template +struct derived_unit_impl; + +/** + * @brief A canonical representation of a unit + * + * A canonical representation of a unit consists of: + * - a reference unit being the result of extraction of all the intermediate derived units, + * - a magnitude being a product of all the prefixes and magnitudes of extracted scaled units. + * + * All units having the same canonical unit are deemed equal. + * All units having the same reference unit are convertible (their magnitude may differ and + * is used during conversion). + * + * @tparam U a unit to use as a `reference_unit` + * @tparam M a Magnitude representing an absolute scaling factor of this unit + */ +template +struct canonical_unit { + M mag; + U reference_unit; +}; + +#if MP_UNITS_COMP_CLANG + +template +canonical_unit(M, U) -> canonical_unit; + +#endif + +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); + +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&); + +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power&); + +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit_impl&); + +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit_impl&); + +template +struct unit_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {}; + +template +using type_list_of_unit_less = expr_less; + +} // namespace detail + +// TODO this should really be in the `details` namespace but is used in `chrono.h` (a part of mp_units.systems) +// Even though it is not exported, it is visible to the other module via ADL +[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return detail::get_canonical_unit_impl(u, u); } + +namespace detail { + +template +[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2) +{ + if constexpr (is_same_v) + return true; + else + return is_same_v; +} + + +struct unit_interface { + /** + * Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned. + */ + template + [[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator*(M, U u) + { + if constexpr (std::is_same_v)>>) + return u; + else + return scaled_unit{}; + } + + [[nodiscard]] friend consteval Unit auto operator*(Unit auto, Magnitude auto) +#if __cpp_deleted_function + = delete("To scale a unit use `mag * unit` syntax"); +#else + = delete; +#endif + + /** + * Returns the result of multiplication with an inverse unit. + */ + template + [[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u) + { + return mag * inverse(u); + } + + /** + * `scaled_unit` specializations have priority in this operation. This means that the library framework + * prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed + * to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation. + */ + template + [[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs rhs) + { + if constexpr (is_specialization_of_scaled_unit && is_specialization_of_scaled_unit) + return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit); + else if constexpr (is_specialization_of_scaled_unit) + return Lhs::mag * (Lhs::reference_unit * rhs); + else if constexpr (is_specialization_of_scaled_unit) + return Rhs::mag * (lhs * Rhs::reference_unit); + else + return expr_multiply(lhs, rhs); + } + + /** + * `scaled_unit` specializations have priority in this operation. This means that the library framework + * prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed + * to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation. + */ + template + [[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs rhs) + { + if constexpr (is_specialization_of_scaled_unit && is_specialization_of_scaled_unit) + return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit); + else if constexpr (is_specialization_of_scaled_unit) + return Lhs::mag * (Lhs::reference_unit / rhs); + else if constexpr (is_specialization_of_scaled_unit) + return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit); + else + return expr_divide(lhs, rhs); + } + + [[nodiscard]] friend consteval bool operator==(Unit auto lhs, Unit auto rhs) + { + auto canonical_lhs = get_canonical_unit(lhs); + auto canonical_rhs = get_canonical_unit(rhs); + return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) && + canonical_lhs.mag == canonical_rhs.mag; + } +}; + template struct propagate_point_origin {}; @@ -65,7 +214,7 @@ struct propagate_point_origin { }; template -struct scaled_unit_impl : detail::propagate_point_origin { +struct scaled_unit_impl : detail::unit_interface, detail::propagate_point_origin { using _base_type_ = scaled_unit_impl; // exposition only static constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M; static constexpr U reference_unit{}; @@ -87,9 +236,6 @@ struct scaled_unit final : detail::scaled_unit_impl {}; namespace detail { -template -inline constexpr bool is_specialization_of_scaled_unit = false; - template inline constexpr bool is_specialization_of_scaled_unit> = true; @@ -142,7 +288,7 @@ struct named_unit; */ template requires(!Symbol.empty()) && detail::BaseDimension> -struct named_unit { +struct named_unit : detail::unit_interface { using _base_type_ = named_unit; // exposition only static constexpr auto symbol = Symbol; ///< Unique base unit identifier static constexpr auto quantity_spec = QS; @@ -150,7 +296,7 @@ struct named_unit { template requires(!Symbol.empty()) && detail::BaseDimension> -struct named_unit { +struct named_unit : detail::unit_interface { using _base_type_ = named_unit; // exposition only static constexpr auto symbol = Symbol; ///< Unique base unit identifier static constexpr auto quantity_spec = QS; @@ -169,7 +315,7 @@ struct named_unit { */ template requires(!Symbol.empty()) -struct named_unit { +struct named_unit : detail::unit_interface { using _base_type_ = named_unit; // exposition only static constexpr auto symbol = Symbol; ///< Unique base unit identifier }; @@ -258,7 +404,7 @@ template struct is_one : std::false_type {}; template -struct derived_unit_impl : detail::expr_fractions { +struct derived_unit_impl : detail::unit_interface, detail::expr_fractions { using _base_type_ = derived_unit_impl; // exposition only }; @@ -326,45 +472,6 @@ namespace detail { template<> struct is_one : std::true_type {}; -/** - * @brief A canonical representation of a unit - * - * A canonical representation of a unit consists of: - * - a reference unit being the result of extraction of all the intermediate derived units, - * - a magnitude being a product of all the prefixes and magnitudes of extracted scaled units. - * - * All units having the same canonical unit are deemed equal. - * All units having the same reference unit are convertible (their magnitude may differ and - * is used during conversion). - * - * @tparam U a unit to use as a `reference_unit` - * @tparam M a Magnitude representing an absolute scaling factor of this unit - */ -template -struct canonical_unit { - M mag; - U reference_unit; -}; - -#if MP_UNITS_COMP_CLANG - -template -canonical_unit(M, U) -> canonical_unit; - -#endif - -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); - -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&); - -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power&); - -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit_impl&); - template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit_impl&) { @@ -422,113 +529,6 @@ template return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit}; } -template -struct unit_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {}; - -template -using type_list_of_unit_less = expr_less; - -} // namespace detail - -// TODO this should really be in the `details` namespace but is used in `chrono.h` (a part of mp_units.systems) -// Even though it is not exported, it is visible to the other module via ADL -[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return detail::get_canonical_unit_impl(u, u); } - -MP_UNITS_EXPORT_BEGIN - -// Operators - -/** - * Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned. - */ -template -[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(M, U u) -{ - if constexpr (std::is_same_v)>>) - return u; - else - return scaled_unit{}; -} - -[[nodiscard]] consteval Unit auto operator*(Unit auto, Magnitude auto) -#if __cpp_deleted_function - = delete("To scale a unit use `mag * unit` syntax"); -#else - = delete; -#endif - -/** - * Returns the result of multiplication with an inverse unit. - */ -template -[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u) -{ - return mag * inverse(u); -} - -/** - * `scaled_unit` specializations have priority in this operation. This means that the library framework - * prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed - * to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation. - */ -template -[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs rhs) -{ - if constexpr (detail::is_specialization_of_scaled_unit && detail::is_specialization_of_scaled_unit) - return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit); - else if constexpr (detail::is_specialization_of_scaled_unit) - return Lhs::mag * (Lhs::reference_unit * rhs); - else if constexpr (detail::is_specialization_of_scaled_unit) - return Rhs::mag * (lhs * Rhs::reference_unit); - else - return detail::expr_multiply(lhs, rhs); -} - -/** - * `scaled_unit` specializations have priority in this operation. This means that the library framework - * prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed - * to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation. - */ -template -[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs rhs) -{ - if constexpr (detail::is_specialization_of_scaled_unit && detail::is_specialization_of_scaled_unit) - return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit); - else if constexpr (detail::is_specialization_of_scaled_unit) - return Lhs::mag * (Lhs::reference_unit / rhs); - else if constexpr (detail::is_specialization_of_scaled_unit) - return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit); - else - return detail::expr_divide(lhs, rhs); -} - -[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return one / u; } - -MP_UNITS_EXPORT_END - -namespace detail { - -template -[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2) -{ - if constexpr (is_same_v) - return true; - else - return is_same_v; -} - -} // namespace detail - -MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs) -{ - auto canonical_lhs = get_canonical_unit(lhs); - auto canonical_rhs = get_canonical_unit(rhs); - return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) && - canonical_lhs.mag == canonical_rhs.mag; -} - -namespace detail { - template inline constexpr bool is_specialization_of_derived_unit = false; @@ -539,6 +539,8 @@ inline constexpr bool is_specialization_of_derived_unit> = MP_UNITS_EXPORT_BEGIN +[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return one / u; } + /** * @brief Computes the value of a unit raised to the `Num/Den` power * diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index e68935be..5f7088cd 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -33,8 +33,7 @@ namespace mp_units { namespace detail { -template -inline constexpr bool is_unit = false; +struct unit_interface; } // namespace detail @@ -44,7 +43,7 @@ inline constexpr bool is_unit = false; * Satisfied by all unit types provided by the library. */ MP_UNITS_EXPORT template -concept Unit = detail::is_unit; +concept Unit = std::derived_from && std::is_final_v; template struct scaled_unit; @@ -122,24 +121,6 @@ struct prefixed_unit; namespace detail { -template -void is_unit_impl(const scaled_unit*); - -template -void is_unit_impl(const named_unit*); - -template -void is_unit_impl(const prefixed_unit*); - -template -void is_unit_impl(const derived_unit*); - -void is_unit_impl(const one*); - -template - requires requires(T* t) { is_unit_impl(t); } && std::is_final_v -inline constexpr bool is_unit = true; - template [[nodiscard]] consteval bool has_associated_quantity(U); diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index 26d76b42..769d92ae 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -58,17 +58,6 @@ static_assert(!detail::BaseDimension>); static_assert(!detail::BaseDimension); static_assert(!detail::BaseDimension); -// DerivedDimension -static_assert(detail::DerivedDimension); -static_assert(detail::DerivedDimension); -static_assert(detail::DerivedDimension(isq::dim_length))>); -static_assert(detail::DerivedDimension>>); -static_assert(detail::DerivedDimension); -static_assert(detail::DerivedDimension>); -static_assert(!detail::DerivedDimension); -static_assert(!detail::DerivedDimension); -static_assert(!detail::DerivedDimension); - // Dimension static_assert(Dimension); static_assert(Dimension); diff --git a/test/static/dimension_test.cpp b/test/static/dimension_test.cpp index dc69eab7..5564a5f1 100644 --- a/test/static/dimension_test.cpp +++ b/test/static/dimension_test.cpp @@ -62,14 +62,10 @@ inline constexpr auto energy = force * length; // concepts verification static_assert(detail::BaseDimension); static_assert(!detail::BaseDimension>); -static_assert(!detail::DerivedDimension); -static_assert(detail::DerivedDimension>); static_assert(Dimension); static_assert(Dimension>); -static_assert(detail::DerivedDimension); -static_assert(detail::DerivedDimension); // dimension_one -static_assert(detail::BaseDimension); // length +static_assert(detail::BaseDimension); // length // derived dimension expression template syntax verification static_assert(is_of_type>>);