diff --git a/docs/getting_started/faq.md b/docs/getting_started/faq.md index a0ee6e41..5bd1c0f0 100644 --- a/docs/getting_started/faq.md +++ b/docs/getting_started/faq.md @@ -125,6 +125,30 @@ all the properties of scaled units and is consistent with the rest of the librar [the Dimensionless Quantities chapter](../users_guide/framework_basics/dimensionless_quantities.md). +## Why do the identifiers for concepts in the library use `CamelCase`? + +Initially, C++20 was meant to use `CamelCase` for all the concept identifiers. All the concepts +from the `std::ranges` library were merged with such names into the standard document draft. +Frustratingly, `CamelCase` concepts got dropped from the C++ standard at the last moment before +releasing C++20. Now, we are facing the predictable consequences of running out of names. + +As long as some concepts in the library could be easily named with a `standard_case` there are +some that are hard to distinguish from the corresponding type names, such as `Quantity`, +`QuantityPoint`, `QuantitySpec`, or `Reference`. This is why we decided to use `CamelCase` +consistently for all the concept identifiers to make it clear when we are talking about a type +or concept identifier. + +However, we are aware that this might be a temporary solution. In case the library gets +standardized, we can expect the ISO C++ Committee to bikeshed/rename all of +the concept identifiers to a `standard_case`, even if it will result in a harder to understand +code. + +!!! note + + In case you have a good idea on how to rename [existing concepts](../users_guide/framework_basics/basic_concepts.md) + to the `standard_case`, please let us know in the associated [GitHub Issue](). + + ## Why Unicode quantity symbols are used by default instead of ASCII-only characters? Both C++ and [ISO 80000](../appendix/references.md#ISO80000) are standardized by the ISO. diff --git a/docs/users_guide/framework_basics/basic_concepts.md b/docs/users_guide/framework_basics/basic_concepts.md index 478ae14f..47ab42f0 100644 --- a/docs/users_guide/framework_basics/basic_concepts.md +++ b/docs/users_guide/framework_basics/basic_concepts.md @@ -427,7 +427,12 @@ for which an instantiation of `quantity_like_traits` type trait yields a valid t - Static data member `reference` that matches the [`Reference`](#Reference) concept, - `rep` type that matches [`RepresentationOf`](#RepresentationOf) concept with the character provided in `reference`, -- `value(T)` static member function returning a raw value of the quantity. +- `to_numerical_value(T)` static member function returning a raw value of the quantity packed in + either `convert_explicitly` or `convert_implicitly` wrapper that enables implicit conversion in + the latter case, +- `from_numerical_value(rep)` static member function returning `T` packed in either `convert_explicitly` + or `convert_implicitly` wrapper that enables implicit conversion in the latter case. + ??? abstract "Examples" @@ -438,10 +443,20 @@ for which an instantiation of `quantity_like_traits` type trait yields a valid t struct mp_units::quantity_like_traits { static constexpr auto reference = si::second; using rep = std::chrono::seconds::rep; - [[nodiscard]] static constexpr rep value(const std::chrono::seconds& q) { return q.count(); } + + [[nodiscard]] static constexpr convert_implicitly to_numerical_value(const std::chrono::seconds& q) + { + return q.count(); + } + + [[nodiscard]] static constexpr convert_implicitly from_numerical_value(const rep& v) + { + return std::chrono::seconds(v); + } }; - quantity q(42s); + quantity q = 42s; + std::chrono::seconds dur = 42 * s; ``` @@ -454,8 +469,13 @@ for which an instantiation of `quantity_point_like_traits` type trait yields a v - Static data member `point_origin` that matches the [`PointOrigin`](#PointOrigin) concept - `rep` type that matches [`RepresentationOf`](#RepresentationOf) concept with the character provided in `reference` -- `quantity_from_origin(T)` static member function returning the `quantity` being the offset of the point - from the origin +- `to_quantity(T)` static member function returning the `quantity` being the offset of the point + from the origin packed in either `convert_explicitly` or `convert_implicitly` wrapper that enables + implicit conversion in the latter case, +- `from_quantity(quantity)` static member function returning `T` packed in either + `convert_explicitly` or `convert_implicitly` wrapper that enables implicit conversion in the latter + case. + ??? abstract "Examples" @@ -464,14 +484,22 @@ for which an instantiation of `quantity_point_like_traits` type trait yields a v ```cpp template struct mp_units::quantity_point_like_traits> { + using T = std::chrono::time_point; static constexpr auto reference = si::second; - static constexpr auto point_origin = chrono_point_origin; + static constexpr struct point_origin : absolute_point_origin {} point_origin{}; using rep = std::chrono::seconds::rep; - [[nodiscard]] static constexpr auto quantity_from_origin(const std::chrono::time_point& qp) + + [[nodiscard]] static constexpr convert_implicitly> to_quantity(const T& qp) { - return quantity{std::chrono::duration_cast(qp.time_since_epoch())}; + return quantity{qp.time_since_epoch()}; + } + + [[nodiscard]] static constexpr convert_implicitly from_quantity(const quantity& q) + { + return T(q); } }; - quantity_point qp(time_point_cast(std::chrono::system_clock::now())); + quantity_point qp = time_point_cast(std::chrono::system_clock::now()); + std::chrono::sys_seconds q = qp + 42 * s; ``` diff --git a/example/include/geographic.h b/example/include/geographic.h index c422fa76..b704dd24 100644 --- a/example/include/geographic.h +++ b/example/include/geographic.h @@ -79,7 +79,7 @@ using longitude = mp_units::quantity_point std::basic_ostream& operator<<(std::basic_ostream& os, const latitude& lat) { - if (is_gt_zero(lat)) + if (is_gteq_zero(lat)) return os << lat.quantity_from_origin() << " N"; else return os << -lat.quantity_from_origin() << " S"; @@ -88,7 +88,7 @@ std::basic_ostream& operator<<(std::basic_ostream& template std::basic_ostream& operator<<(std::basic_ostream& os, const longitude& lon) { - if (is_gt_zero(lon)) + if (is_gteq_zero(lon)) return os << lon.quantity_from_origin() << " E"; else return os << -lon.quantity_from_origin() << " W"; @@ -138,8 +138,8 @@ struct MP_UNITS_STD_FMT::formatter> : auto format(geographic::latitude lat, FormatContext& ctx) { formatter::quantity_type>::format( - is_gt_zero(lat) ? lat.quantity_from(geographic::equator) : -lat.quantity_from(geographic::equator), ctx); - MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gt_zero(lat) ? " N" : "S"); + is_gteq_zero(lat) ? lat.quantity_from(geographic::equator) : -lat.quantity_from(geographic::equator), ctx); + MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gteq_zero(lat) ? " N" : "S"); return ctx.out(); } }; @@ -151,9 +151,10 @@ struct MP_UNITS_STD_FMT::formatter> : auto format(geographic::longitude lon, FormatContext& ctx) { formatter::quantity_type>::format( - is_gt_zero(lon) ? lon.quantity_from(geographic::prime_meridian) : -lon.quantity_from(geographic::prime_meridian), + is_gteq_zero(lon) ? lon.quantity_from(geographic::prime_meridian) + : -lon.quantity_from(geographic::prime_meridian), ctx); - MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gt_zero(lon) ? " E" : " W"); + MP_UNITS_STD_FMT::format_to(ctx.out(), "{}", is_gteq_zero(lon) ? " E" : " W"); return ctx.out(); } }; diff --git a/example/measurement.cpp b/example/measurement.cpp index 719a9017..53971388 100644 --- a/example/measurement.cpp +++ b/example/measurement.cpp @@ -134,11 +134,11 @@ void example() using namespace mp_units; using namespace mp_units::si::unit_symbols; - const auto a = isq::acceleration(measurement{9.8, 0.1} * m / s2); - const auto t = measurement{1.2, 0.1} * s; + const auto acceleration = isq::acceleration(measurement{9.8, 0.1} * m / s2); + const auto time = measurement{1.2, 0.1} * s; - const QuantityOf auto v = a * t; - std::cout << a << " * " << t << " = " << v << " = " << v.in(km / h) << '\n'; + const QuantityOf auto velocity = acceleration * time; + std::cout << acceleration << " * " << time << " = " << velocity << " = " << velocity.in(km / h) << '\n'; const auto length = measurement{123., 1.} * m; std::cout << "10 * " << length << " = " << 10 * length << '\n'; diff --git a/src/core-fmt/include/mp-units/bits/fmt.h b/src/core-fmt/include/mp-units/bits/fmt.h index 95ef4549..973f01ad 100644 --- a/src/core-fmt/include/mp-units/bits/fmt.h +++ b/src/core-fmt/include/mp-units/bits/fmt.h @@ -103,9 +103,9 @@ struct width_checker { if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative width")); } return static_cast(value); - } else { - MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("width is not integer")); } + MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("width is not integer")); + return 0; // should never happen } }; @@ -118,9 +118,9 @@ struct precision_checker { if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative precision")); } return static_cast(value); - } else { - MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("precision is not integer")); } + MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("precision is not integer")); + return 0; // should never happen } }; @@ -230,6 +230,7 @@ template S, typename IDHandler> return begin; } MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid format string")); + return begin; // should never happen } template S, typename IDHandler> diff --git a/src/core/include/mp-units/bits/get_common_base.h b/src/core/include/mp-units/bits/get_common_base.h index 0c232dc1..97ad5fbd 100644 --- a/src/core/include/mp-units/bits/get_common_base.h +++ b/src/core/include/mp-units/bits/get_common_base.h @@ -58,8 +58,8 @@ template template [[nodiscard]] consteval auto have_common_base(A a, B b) { - constexpr int a_length = hierarchy_path_length(A{}); - constexpr int b_length = hierarchy_path_length(B{}); + constexpr std::size_t a_length = hierarchy_path_length(A{}); + constexpr std::size_t b_length = hierarchy_path_length(B{}); if constexpr (a_length > b_length) return have_common_base_in_hierarchy_of_equal_length(hierarchy_path_advance(a), b); else diff --git a/src/core/include/mp-units/bits/quantity_concepts.h b/src/core/include/mp-units/bits/quantity_concepts.h index 437d64e3..56c652b9 100644 --- a/src/core/include/mp-units/bits/quantity_concepts.h +++ b/src/core/include/mp-units/bits/quantity_concepts.h @@ -74,15 +74,19 @@ concept QuantityOf = Quantity && ReferenceOf -concept QuantityLike = requires(T q) { +concept QuantityLike = requires { quantity_like_traits::reference; requires Reference::reference)>>; typename quantity_like_traits::rep; requires RepresentationOf::rep, get_quantity_spec(quantity_like_traits::reference).character>; +} && requires(T q, typename quantity_like_traits::rep v) { { - make_quantity::reference>(quantity_like_traits::value(q)) - } -> std::same_as::reference, typename quantity_like_traits::rep>>; + quantity_like_traits::to_numerical_value(q) + } -> detail::ConversionSpecOf::rep>; + { + quantity_like_traits::from_numerical_value(v) + } -> detail::ConversionSpecOf; }; } // namespace mp_units diff --git a/src/core/include/mp-units/bits/quantity_point_concepts.h b/src/core/include/mp-units/bits/quantity_point_concepts.h index df4b1a24..b2d1091c 100644 --- a/src/core/include/mp-units/bits/quantity_point_concepts.h +++ b/src/core/include/mp-units/bits/quantity_point_concepts.h @@ -173,7 +173,7 @@ concept QuantityPointOf = * all quantity_point-specific information. */ template -concept QuantityPointLike = requires(T qp) { +concept QuantityPointLike = requires { quantity_point_like_traits::reference; requires Reference::reference)>>; quantity_point_like_traits::point_origin; @@ -181,13 +181,15 @@ concept QuantityPointLike = requires(T qp) { typename quantity_point_like_traits::rep; requires RepresentationOf::rep, get_quantity_spec(quantity_point_like_traits::reference).character>; - requires Quantity::quantity_from_origin(qp))>>; +} && requires(T qp, quantity::reference, typename quantity_point_like_traits::rep> q) { { - make_quantity_point::point_origin>( - quantity_point_like_traits::quantity_from_origin(qp)) - } - -> std::same_as::reference, quantity_point_like_traits::point_origin, - typename quantity_point_like_traits::rep>>; + quantity_point_like_traits::to_quantity(qp) + } -> detail::ConversionSpecOf< + quantity::reference, typename quantity_point_like_traits::rep>>; + + { + quantity_point_like_traits::from_quantity(q) + } -> detail::ConversionSpecOf; }; } // namespace mp_units diff --git a/src/core/include/mp-units/customization_points.h b/src/core/include/mp-units/customization_points.h index e4c16916..18f2b594 100644 --- a/src/core/include/mp-units/customization_points.h +++ b/src/core/include/mp-units/customization_points.h @@ -132,11 +132,42 @@ struct quantity_values { } }; +template +struct convert_explicitly { + using value_type = T; + T value; + constexpr explicit(false) convert_explicitly(T v) noexcept(std::is_nothrow_constructible_v) : value(std::move(v)) + { + } +}; + +template +struct convert_implicitly { + using value_type = T; + T value; + constexpr explicit(false) convert_implicitly(T v) noexcept(std::is_nothrow_constructible_v) : value(std::move(v)) + { + } +}; + +namespace detail { + +template +concept ConversionSpec = is_specialization_of || is_specialization_of; + +template +concept ConversionSpecOf = ConversionSpec && std::same_as; + +} // namespace detail + /** * @brief Provides support for external quantity-like types * * The type trait should provide the @c reference object, a type alias @c rep, - * and a static member function @c value(T) that returns the raw value of the quantity. + * and static member functions @c to_numerical_value(T) that returns the raw value + * of the quantity and @c from_numerical_value(rep) that returns @c T from @c rep. + * Both return types should be encapsulated in either @c convert_explicitly or + * @c convert_implicitly to specify if the conversion is allowed to happen implicitly. * * Usage example can be found in @c units/chrono.h header file. * @@ -149,8 +180,11 @@ struct quantity_like_traits; * @brief Provides support for external quantity point-like types * * The type trait should provide nested @c reference and @c origin objects, - * a type alias @c rep, and a static member function @c quantity_from_origin(T) that will - * return the quantity being the offset of the point from the origin. + * a type alias @c rep, and static member functions @c to_quantity(T) that returns + * the quantity being the offset of the point from the origin and + * @c from_quantity(quantity) that returns @c T form this quantity. + * Both return types should be encapsulated in either @c convert_explicitly or + * @c convert_implicitly to specify if the conversion is allowed to happen implicitly. * * Usage example can be found in @c units/chrono.h header file. * diff --git a/src/core/include/mp-units/quantity.h b/src/core/include/mp-units/quantity.h index 8f50edfe..2ebc1edd 100644 --- a/src/core/include/mp-units/quantity.h +++ b/src/core/include/mp-units/quantity.h @@ -133,15 +133,17 @@ public: template requires detail::QuantityConvertibleTo< quantity::reference, typename quantity_like_traits::rep>, quantity> - constexpr explicit quantity(const Q& q) : - quantity(make_quantity::reference>(quantity_like_traits::value(q))) + constexpr explicit(is_specialization_of::to_numerical_value(std::declval())), + convert_explicitly> || + !std::convertible_to) quantity(const Q& q) : + quantity(make_quantity::reference>(quantity_like_traits::to_numerical_value(q).value)) { } quantity& operator=(const quantity&) = default; quantity& operator=(quantity&&) = default; - // conversions + // unit conversions template U> requires detail::QuantityConvertibleTo> [[nodiscard]] constexpr Quantity auto in(U) const @@ -189,6 +191,31 @@ public: return (*this).force_in(U{}).numerical_value_; } + // conversion operators + template> + requires detail::QuantityConvertibleTo< + quantity, quantity::reference, typename quantity_like_traits::rep>> + [[nodiscard]] explicit(is_specialization_of::from_numerical_value(numerical_value_)), + convert_explicitly> || + !std::convertible_to) constexpr + operator Q_() const& noexcept(noexcept(quantity_like_traits::from_numerical_value(numerical_value_)) && + std::is_nothrow_copy_constructible_v) + { + return quantity_like_traits::from_numerical_value(numerical_value_).value; + } + + template> + requires detail::QuantityConvertibleTo< + quantity, quantity::reference, typename quantity_like_traits::rep>> + [[nodiscard]] explicit(is_specialization_of::from_numerical_value(numerical_value_)), + convert_explicitly> || + !std::convertible_to) constexpr + operator Q_() && noexcept(noexcept(quantity_like_traits::from_numerical_value(numerical_value_)) && + std::is_nothrow_move_constructible_v) + { + return quantity_like_traits::from_numerical_value(std::move(numerical_value_)).value; + } + // member unary operators [[nodiscard]] constexpr Quantity auto operator+() const requires requires(rep v) { @@ -365,7 +392,9 @@ private: // CTAD template -explicit quantity(Q) -> quantity::reference, typename quantity_like_traits::rep>; +explicit( + is_specialization_of::to_numerical_value(std::declval())), convert_explicitly>) + quantity(Q) -> quantity::reference, typename quantity_like_traits::rep>; // binary operators on quantities template diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index 3e158770..6cf8308b 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -128,8 +128,13 @@ public: std::convertible_to< quantity::reference, typename quantity_point_like_traits::rep>, quantity_type> - constexpr explicit quantity_point(const QP& qp) : - quantity_from_origin_(quantity_point_like_traits::quantity_from_origin(qp)) + constexpr explicit( + is_specialization_of::to_quantity(std::declval())), + convert_explicitly> || + !std::convertible_to< + quantity::reference, typename quantity_point_like_traits::rep>, quantity_type>) + quantity_point(const QP& qp) : + quantity_from_origin_(quantity_point_like_traits::to_quantity(qp).value) { } @@ -137,7 +142,8 @@ public: quantity_point& operator=(quantity_point&&) = default; template NewPO> - [[nodiscard]] constexpr QuantityPointOf auto point_for(NewPO new_origin) const + [[nodiscard]] constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(QuantityPointOf) auto point_for( + NewPO new_origin) const { if constexpr (is_same_v>) return *this; @@ -168,6 +174,7 @@ public: return *this - PO2{}; } + // unit conversions template U> requires detail::QuantityConvertibleTo> [[nodiscard]] constexpr QuantityPoint auto in(U) const @@ -182,6 +189,39 @@ public: return make_quantity_point(quantity_ref_from(PO).force_in(U{})); } + // conversion operators + template> + requires std::same_as, + std::remove_const_t::point_origin)>> && + std::convertible_to::reference, + typename quantity_point_like_traits::rep>> + [[nodiscard]] explicit( + is_specialization_of::from_quantity(quantity_from_origin_)), + convert_explicitly> || + !std::convertible_to::reference, + typename quantity_point_like_traits::rep>>) constexpr + operator QP_() const& noexcept(noexcept(quantity_point_like_traits::from_quantity(quantity_from_origin_)) && + std::is_nothrow_copy_constructible_v) + { + return quantity_point_like_traits::from_quantity(quantity_from_origin_).value; + } + + template> + requires std::same_as, + std::remove_const_t::point_origin)>> && + std::convertible_to::reference, + typename quantity_point_like_traits::rep>> + [[nodiscard]] explicit( + is_specialization_of::from_quantity(quantity_from_origin_)), + convert_explicitly> || + !std::convertible_to::reference, + typename quantity_point_like_traits::rep>>) constexpr + operator QP_() && noexcept(noexcept(quantity_point_like_traits::from_quantity(quantity_from_origin_)) && + std::is_nothrow_move_constructible_v) + { + return quantity_point_like_traits::from_quantity(std::move(quantity_from_origin_)).value; + } + // member unary operators template friend constexpr decltype(auto) operator++(QP&& qp) @@ -249,9 +289,11 @@ private: // CTAD template -explicit quantity_point(QP) - -> quantity_point::reference, quantity_point_like_traits::point_origin, - typename quantity_point_like_traits::rep>; +explicit( + is_specialization_of::to_quantity(std::declval())), convert_explicitly>) + quantity_point(QP) + -> quantity_point::reference, quantity_point_like_traits::point_origin, + typename quantity_point_like_traits::rep>; template // TODO simplify when gcc catches up diff --git a/src/core/include/mp-units/quantity_spec.h b/src/core/include/mp-units/quantity_spec.h index 2adafe91..b85db0cc 100644 --- a/src/core/include/mp-units/quantity_spec.h +++ b/src/core/include/mp-units/quantity_spec.h @@ -41,12 +41,12 @@ namespace detail { template requires(!AssociatedUnit) || UnitOf -[[nodiscard]] consteval Reference auto make_reference(QS qs, U u) +[[nodiscard]] consteval Reference auto make_reference(QS, U u) { if constexpr (detail::QuantityKindSpec) return u; else - return reference{}; + return reference{}; } // TODO revise the note in the below comment @@ -494,9 +494,10 @@ template // Operators -[[nodiscard]] consteval QuantitySpec auto operator*(QuantitySpec auto lhs, QuantitySpec auto rhs) +template +[[nodiscard]] consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs) { - return detail::clone_kind_of( + return detail::clone_kind_of( detail::expr_multiply( remove_kind(lhs), remove_kind(rhs))); } @@ -504,7 +505,7 @@ template template [[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs) { - return detail::clone_kind_of( + return detail::clone_kind_of( detail::expr_divide( remove_kind(lhs), remove_kind(rhs))); } @@ -548,7 +549,7 @@ template // `2 * one * (2 * one)` should compare to `4 * one` // `2 * rad * (2 * rad)` should compare to `4 * rad^2` // all are dimensionless quantities :-( - if constexpr (Num == 0 || q == dimensionless) + if constexpr (Num == 0 || Q{} == dimensionless) return dimensionless; else if constexpr (ratio{Num, Den} == 1) return q; @@ -773,7 +774,7 @@ template template [[nodiscard]] consteval auto explode(Q q) { - constexpr auto c = get_complexity(q); + constexpr auto c = get_complexity(Q{}); if constexpr (c > Complexity) return explode(q, type_list_sort{}, type_list_sort{}); @@ -784,9 +785,9 @@ template template [[nodiscard]] consteval auto explode(Q q) { - constexpr auto c = get_complexity(q); + constexpr auto c = get_complexity(Q{}); if constexpr (c > Complexity && requires { Q::_equation_; }) { - constexpr auto res = explode_to_equation(q); + constexpr auto res = explode_to_equation(Q{}); return explode(res.equation).common_convertibility_with(res); } else return explode_result{q}; @@ -879,10 +880,10 @@ extract_results(bool, From = {}, To = {}, prepend_rest = {}, Elem = {}) -> extra #endif template -[[nodiscard]] consteval auto extract_convertible_quantities(From from, To to) +[[nodiscard]] consteval auto extract_convertible_quantities(From, To) { - constexpr auto qfrom = map_power(from); - constexpr auto qto = map_power(to); + constexpr auto qfrom = map_power(From{}); + constexpr auto qto = map_power(To{}); if constexpr (qfrom.dimension == qto.dimension) { if constexpr (is_specialization_of_power && is_specialization_of_power) { constexpr auto cr = common_ratio(From::exponent, To::exponent); @@ -899,8 +900,8 @@ template else return std::tuple{Q{}, ratio{1}}; }; - constexpr auto from_norm = normalize(from); - constexpr auto to_norm = normalize(to); + constexpr auto from_norm = normalize(From{}); + constexpr auto to_norm = normalize(To{}); constexpr auto from_factor = std::get<0>(from_norm); constexpr auto from_exp = std::get<1>(from_norm); constexpr auto to_factor = std::get<0>(to_norm); @@ -1329,11 +1330,11 @@ template if constexpr (From::dimension != To::dimension) return no; - else if constexpr (from == to) + else if constexpr (From{} == To{}) return yes; else if constexpr (QuantityKindSpec || QuantityKindSpec) { - constexpr auto from_kind = get_kind(from); - constexpr auto to_kind = get_kind(to); + constexpr auto from_kind = get_kind(From{}); + constexpr auto to_kind = get_kind(To{}); constexpr auto exploded_kind_result = [](specs_convertible_result res) { using enum specs_convertible_result; return res == no ? no : yes; @@ -1348,21 +1349,21 @@ template else return exploded_kind_result( convertible_impl(from_kind, get_kind(explode(to_kind).quantity))); - } else if constexpr (NestedQuantityKindSpecOf && get_kind(to) == to) + } else if constexpr (NestedQuantityKindSpecOf && get_kind(To{}) == To{}) return yes; else if constexpr (NamedQuantitySpec && NamedQuantitySpec) { - if constexpr (have_common_base(from, to)) { + if constexpr (have_common_base(From{}, To{})) { if (std::derived_from) return yes; else return std::derived_from ? explicit_conversion : (get_kind(from) == get_kind(to) ? cast : no); - } else if constexpr (get_kind(from) != get_kind(to)) + } else if constexpr (get_kind(From{}) != get_kind(To{})) return no; - else if constexpr (get_complexity(from) != get_complexity(to)) { - if constexpr (get_complexity(from) > get_complexity(to)) + else if constexpr (get_complexity(From{}) != get_complexity(To{})) { + if constexpr (get_complexity(From{}) > get_complexity(To{})) return convertible_impl(explode(from).quantity, to); else { - constexpr auto res = explode(to); + auto res = explode(to); return min(res.result, convertible_impl(from, res.quantity)); } } @@ -1373,7 +1374,7 @@ template if constexpr (NamedQuantitySpec>) return convertible_impl(res.quantity, to); else if constexpr (requires { to._equation_; }) { - constexpr auto eq = explode_to_equation(to); + auto eq = explode_to_equation(to); return min(eq.result, convertible_impl(res.quantity, eq.equation)); } else return are_ingredients_convertible(from, to); @@ -1443,16 +1444,16 @@ template template [[nodiscard]] consteval QuantitySpec auto get_kind(Q q) { - auto defined_as_kind = [](auto qq) { + auto defined_as_kind = [](QQ qq) { if constexpr (requires { detail::defined_as_kind(qq); }) - return detail::defined_as_kind(qq); + return detail::defined_as_kind(QQ{}); else return false; }; if constexpr (detail::QuantityKindSpec) { return remove_kind(q); - } else if constexpr (defined_as_kind(q)) { + } else if constexpr (defined_as_kind(Q{})) { return q; } else if constexpr (requires { Q::_parent_; }) { return get_kind(Q::_parent_); @@ -1475,25 +1476,25 @@ template using QQ2 = std::remove_const_t; if constexpr (is_same_v) return q1; - else if constexpr (detail::NestedQuantityKindSpecOf) + else if constexpr (detail::NestedQuantityKindSpecOf) return remove_kind(q1); - else if constexpr (detail::NestedQuantityKindSpecOf) + else if constexpr (detail::NestedQuantityKindSpecOf) return remove_kind(q2); else if constexpr ((detail::QuantityKindSpec && !detail::QuantityKindSpec) || (detail::IntermediateDerivedQuantitySpec && detail::NamedQuantitySpec && - implicitly_convertible(q1, q2))) + implicitly_convertible(Q1{}, Q2{}))) return q2; else if constexpr ((!detail::QuantityKindSpec && detail::QuantityKindSpec) || (detail::NamedQuantitySpec && detail::IntermediateDerivedQuantitySpec && - implicitly_convertible(q2, q1))) + implicitly_convertible(Q2{}, Q1{}))) return q1; - else if constexpr (detail::have_common_base(q1, q2)) + else if constexpr (detail::have_common_base(Q1{}, Q2{})) return detail::get_common_base(q1, q2); - else if constexpr (implicitly_convertible(q1, q2)) + else if constexpr (implicitly_convertible(Q1{}, Q2{})) return q2; - else if constexpr (implicitly_convertible(q2, q1)) + else if constexpr (implicitly_convertible(Q2{}, Q1{})) return q1; - else if constexpr (implicitly_convertible(get_kind(q1), get_kind(q2))) + else if constexpr (implicitly_convertible(get_kind(Q1{}), get_kind(Q2{}))) return get_kind(q2); else return get_kind(q1); diff --git a/src/core/include/mp-units/reference.h b/src/core/include/mp-units/reference.h index 2c1ce7d2..41ac4787 100644 --- a/src/core/include/mp-units/reference.h +++ b/src/core/include/mp-units/reference.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -73,13 +74,21 @@ struct reference { } template +#if MP_UNITS_COMP_MSVC + [[nodiscard]] friend consteval decltype(reference{}) operator*(reference, U2) +#else [[nodiscard]] friend consteval reference operator*(reference, U2) +#endif { return {}; } template +#if MP_UNITS_COMP_MSVC + [[nodiscard]] friend consteval decltype(reference{}) operator*(U1, reference) +#else [[nodiscard]] friend consteval reference operator*(U1, reference) +#endif { return {}; } @@ -91,13 +100,21 @@ struct reference { } template +#if MP_UNITS_COMP_MSVC + [[nodiscard]] friend consteval decltype(reference{}) operator/(reference, U2) +#else [[nodiscard]] friend consteval reference operator/(reference, U2) +#endif { return {}; } template +#if MP_UNITS_COMP_MSVC + [[nodiscard]] friend consteval decltype(reference{}) operator/(U1, reference) +#else [[nodiscard]] friend consteval reference operator/(U1, reference) +#endif { return {}; } diff --git a/src/core/include/mp-units/system_reference.h b/src/core/include/mp-units/system_reference.h index 32568569..59f4769c 100644 --- a/src/core/include/mp-units/system_reference.h +++ b/src/core/include/mp-units/system_reference.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -63,7 +64,11 @@ struct system_reference { template requires(convertible(coherent_unit, U{})) +#if MP_UNITS_COMP_MSVC + [[nodiscard]] constexpr decltype(reference{}) operator[](U) const +#else [[nodiscard]] constexpr reference operator[](U) const +#endif { return {}; } diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index 760bcfc4..50c38a2c 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -354,9 +354,9 @@ template template [[nodiscard]] consteval auto get_canonical_unit_impl(const type_list&) { - auto mag = (mp_units::mag<1> * ... * get_canonical_unit_impl(Us{}, Us{}).mag); + auto m = (mp_units::mag<1> * ... * get_canonical_unit_impl(Us{}, Us{}).mag); auto u = (one * ... * get_canonical_unit_impl(Us{}, Us{}).reference_unit); - return canonical_unit{mag, u}; + return canonical_unit{m, u}; } template @@ -402,12 +402,12 @@ using type_list_of_unit_less = expr_less; * Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned. */ template -[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(M mag, const U u) +[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(M, const U u) { - if constexpr (mag == mp_units::mag<1>) + if constexpr (std::is_same_v)>>) return u; else - return scaled_unit{}; + return scaled_unit{}; } [[nodiscard]] consteval Unit auto operator*(Unit auto, Magnitude auto) = delete; diff --git a/src/utility/include/mp-units/chrono.h b/src/utility/include/mp-units/chrono.h index f868d84b..d71372e3 100644 --- a/src/utility/include/mp-units/chrono.h +++ b/src/utility/include/mp-units/chrono.h @@ -64,7 +64,18 @@ template struct quantity_like_traits> { static constexpr auto reference = detail::time_unit_from_chrono_period(); using rep = Rep; - [[nodiscard]] static constexpr rep value(const std::chrono::duration& q) { return q.count(); } + + [[nodiscard]] static constexpr convert_implicitly to_numerical_value( + const std::chrono::duration& q) noexcept(std::is_nothrow_copy_constructible_v) + { + return q.count(); + } + + [[nodiscard]] static constexpr convert_implicitly> from_numerical_value( + const rep& v) noexcept(std::is_nothrow_copy_constructible_v) + { + return std::chrono::duration(v); + } }; template @@ -77,14 +88,22 @@ inline constexpr chrono_point_origin_ chrono_point_origin; template struct quantity_point_like_traits>> { + using T = std::chrono::time_point>; static constexpr auto reference = detail::time_unit_from_chrono_period(); static constexpr auto point_origin = chrono_point_origin; using rep = Rep; - [[nodiscard]] static constexpr quantity quantity_from_origin( - const std::chrono::time_point>& qp) + + [[nodiscard]] static constexpr convert_implicitly> to_quantity(const T& qp) noexcept( + std::is_nothrow_copy_constructible_v) { return quantity{qp.time_since_epoch()}; } + + [[nodiscard]] static constexpr convert_implicitly from_quantity(const quantity& q) noexcept( + std::is_nothrow_copy_constructible_v) + { + return T(q); + } }; template Q> @@ -92,7 +111,7 @@ template Q> { constexpr auto canonical = detail::get_canonical_unit(Q::unit); constexpr ratio r = as_ratio(canonical.mag); - return std::chrono::duration>{q.numerical_value_ref_in(Q::unit)}; + return std::chrono::duration>{q}; } template QP> diff --git a/test/unit_test/static/chrono_test.cpp b/test/unit_test/static/chrono_test.cpp index d861e25a..b8c61fa5 100644 --- a/test/unit_test/static/chrono_test.cpp +++ b/test/unit_test/static/chrono_test.cpp @@ -52,40 +52,40 @@ static_assert(!QuantityPoint); // construction - same rep type static_assert( std::constructible_from, std::chrono::seconds>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, std::chrono::hours>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, std::chrono::hours>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(!std::constructible_from, std::chrono::seconds>); static_assert(!std::convertible_to>); static_assert( std::constructible_from, sys_seconds>); static_assert( !std::constructible_from, sys_seconds>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, sys_days>); static_assert(!std::constructible_from, sys_days>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, sys_days>); static_assert(!std::constructible_from, sys_days>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(!std::constructible_from, sys_seconds>); static_assert(!std::convertible_to>); // construction - different rep type (integral to a floating-point) static_assert(std::constructible_from, std::chrono::seconds>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, std::chrono::hours>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, std::chrono::seconds>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, sys_seconds>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, sys_days>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(std::constructible_from, sys_seconds>); -static_assert(!std::convertible_to>); +static_assert(std::convertible_to>); static_assert(quantity{1s} == 1 * s); static_assert(quantity{1s} == 1 * s); diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 41acddeb..46e0b6d1 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -525,7 +525,7 @@ static_assert(!std::convertible_to, static_assert( std::constructible_from>, sys_seconds>); static_assert( - !std::convertible_to>>); + std::convertible_to>>); // incompatible origin static_assert(