diff --git a/docs/users_guide/framework_basics/concepts.md b/docs/users_guide/framework_basics/concepts.md index 33aa24c3..797e7201 100644 --- a/docs/users_guide/framework_basics/concepts.md +++ b/docs/users_guide/framework_basics/concepts.md @@ -250,15 +250,15 @@ class template. `QuantityLike` concept provides interoperability with other libraries and is satisfied by a type `T` for which an instantiation of `quantity_like_traits` type trait yields a valid type that provides: -- Static data member `reference` that matches the [`Reference`](#Reference) concept, +- `reference` static data member that matches the [`Reference`](#Reference) concept, - `rep` type that matches [`RepresentationOf`](#RepresentationOf) concept with the character provided - in `reference`. -- `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. - + in `reference`, +- `explicit_import` static data member convertible to `bool` that specifies that the conversion + from `T` to a `quantity` type should happen explicitly (if `true`), +- `explicit_export` static data member convertible to `bool` that specifies that the conversion + from a `quantity` type to `T` should happen explicitly (if `true`), +- `to_numerical_value(T)` static member function returning a raw value of the quantity, +- `from_numerical_value(rep)` static member function returning `T`. ??? abstract "Examples" @@ -268,14 +268,16 @@ for which an instantiation of `quantity_like_traits` type trait yields a valid t template<> struct mp_units::quantity_like_traits { static constexpr auto reference = si::second; + static constexpr bool explicit_import = false; + static constexpr bool explicit_export = false; using rep = std::chrono::seconds::rep; - [[nodiscard]] static constexpr convert_implicitly to_numerical_value(const std::chrono::seconds& d) + [[nodiscard]] static constexpr rep to_numerical_value(const std::chrono::seconds& d) { return d.count(); } - [[nodiscard]] static constexpr convert_implicitly from_numerical_value(const rep& v) + [[nodiscard]] static constexpr std::chrono::seconds from_numerical_value(const rep& v) { return std::chrono::seconds(v); } @@ -291,15 +293,17 @@ for which an instantiation of `quantity_like_traits` type trait yields a valid t `QuantityPointLike` concept provides interoperability with other libraries and is satisfied by a type `T` for which an instantiation of `quantity_point_like_traits` type trait yields a valid type that provides: -- Static data member `reference` that matches the [`Reference`](#Reference) concept. -- Static data member `point_origin` that matches the [`PointOrigin`](#PointOrigin) concept. +- `reference` static data member that matches the [`Reference`](#Reference) concept. +- `point_origin` static data member that matches the [`PointOrigin`](#PointOrigin) concept. - `rep` type that matches [`RepresentationOf`](#RepresentationOf) concept with the character provided in `reference`. +- `explicit_import` static data member convertible to `bool` that specifies that the conversion + from `T` to a `quantity_point` type should happen explicitly (if `true`), +- `explicit_export` static data member convertible to `bool` that specifies that the conversion + from a `quantity_point` type to `T` should happen explicitly (if `true`), - `to_numerical_value(T)` static member function returning a raw value of 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_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. + of the point from the origin, +- `from_numerical_value(rep)` static member function returning `T`. ??? abstract "Examples" @@ -309,17 +313,19 @@ 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 struct point_origin_ final : absolute_point_origin {} point_origin{}; + static constexpr bool explicit_import = false; + static constexpr bool explicit_export = false; using rep = std::chrono::seconds::rep; + using T = std::chrono::time_point; - [[nodiscard]] static constexpr convert_implicitly to_numerical_value(const T& tp) + [[nodiscard]] static constexpr rep to_numerical_value(const T& tp) { return tp.time_since_epoch().count(); } - [[nodiscard]] static constexpr convert_implicitly from_numerical_value(const rep& v) + [[nodiscard]] static constexpr T from_numerical_value(const rep& v) { return T(std::chrono::seconds(v)); } diff --git a/docs/users_guide/use_cases/interoperability_with_other_libraries.md b/docs/users_guide/use_cases/interoperability_with_other_libraries.md index 07da96af..dcac471f 100644 --- a/docs/users_guide/use_cases/interoperability_with_other_libraries.md +++ b/docs/users_guide/use_cases/interoperability_with_other_libraries.md @@ -31,9 +31,15 @@ Typically, the implicit conversions are allowed in cases where: In all other scenarios, we should probably enforce explicit conversions. The kinds of inter-library conversions can be easily configured in partial specializations -of conversion traits in the **mp-units** library. To require an explicit conversion, the return -type of the conversion function should be wrapped in `convert_explicitly`. Otherwise, -`convert_implicitly` should be used. +of conversion traits in the **mp-units** library. Conversion traits should provide +a static data member convertible to `bool`. If the value is `true`, then the conversion is +`explicit`. Otherwise, if the value is `false`, implicit conversions will be allowed. +The names of the flags are as follows: + +- `explicit_import` to describe conversion from the external entity to the one in this + library (import case), +- `explicit_export` to describe conversion from the entity in this library to the external one + (export case). ## Quantities conversions @@ -56,12 +62,14 @@ to see the opposite conversions stated explicitly in our code. To enable such interoperability, we must define a partial specialization of the `quantity_like_traits` type trait. Such specialization should provide: -- static data member `reference` that provides the quantity reference (e.g., unit), +- `reference` static data member that provides the quantity reference (e.g., unit), - `rep` type that specifies the underlying storage type, -- `to_numerical_value(T)` static member function returning a quantity's raw value of `rep` type - packed in either `convert_explicitly` or `convert_implicitly` wrapper. -- `from_numerical_value(rep)` static member function returning `T` packed in either `convert_explicitly` - or `convert_implicitly` wrapper. +- `explicit_import` static data member convertible to `bool` that specifies that the conversion + from `T` to a `quantity` type should happen explicitly (if `true`), +- `explicit_export` static data member convertible to `bool` that specifies that the conversion + from a `quantity` type to `T` should happen explicitly (if `true`), +- `to_numerical_value(T)` static member function returning a quantity's raw value of `rep` type, +- `from_numerical_value(rep)` static member function returning `T`. For example, for our `Meter` type, we could provide the following: @@ -69,9 +77,11 @@ For example, for our `Meter` type, we could provide the following: template<> struct mp_units::quantity_like_traits { static constexpr auto reference = si::metre; + static constexpr bool explicit_import = false; + static constexpr bool explicit_export = true; using rep = decltype(Meter::value); - static constexpr convert_implicitly to_numerical_value(Meter m) { return m.value; } - static constexpr convert_explicitly from_numerical_value(rep v) { return Meter{v}; } + static constexpr rep to_numerical_value(Meter m) { return m.value; } + static constexpr Meter from_numerical_value(rep v) { return Meter{v}; } }; ``` @@ -170,15 +180,17 @@ To allow the conversion between our custom `Timestamp` type and the `quantity_po we need to provide the following in the partial specialization of the `quantity_point_like_traits` type trait: -- static data member `reference` that provides the quantity point reference (e.g., unit), -- static data member `point_origin` that specifies the absolute point, which is the beginning of +- `reference` static data member that provides the quantity point reference (e.g., unit), +- `point_origin` static data member that specifies the absolute point, which is the beginning of our measurement scale for our points, - `rep` type that specifies the underlying storage type, +- `explicit_import` static data member convertible to `bool` that specifies that the conversion + from `T` to a `quantity` type should happen explicitly (if `true`), +- `explicit_export` static data member convertible to `bool` that specifies that the conversion + from a `quantity` type to `T` should happen explicitly (if `true`), - `to_numerical_value(T)` static member function returning a raw value of the `quantity` being - the offset of the point from the origin packed in either `convert_explicitly` or `convert_implicitly` - wrapper. -- `from_numerical_value(rep)` static member function returning `T` packed in either `convert_explicitly` - or `convert_implicitly` wrapper. + the offset of the point from the origin, +- `from_numerical_value(rep)` static member function returning `T`. For example, for our `Timestamp` type, we could provide the following: @@ -187,9 +199,11 @@ template<> struct mp_units::quantity_point_like_traits { static constexpr auto reference = si::second; static constexpr auto point_origin = default_point_origin(reference); + static constexpr bool explicit_import = false; + static constexpr bool explicit_export = true; using rep = decltype(Timestamp::seconds); - static constexpr convert_implicitly to_numerical_value(Timestamp ts) { return ts.seconds; } - static constexpr convert_explicitly from_numerical_value(rep v) { return Timestamp(v); } + static constexpr rep to_numerical_value(Timestamp ts) { return ts.seconds; } + static constexpr Timestamp from_numerical_value(rep v) { return Timestamp(v); } }; ``` diff --git a/src/core/include/mp-units/framework/customization_points.h b/src/core/include/mp-units/framework/customization_points.h index 59636659..29dcda6a 100644 --- a/src/core/include/mp-units/framework/customization_points.h +++ b/src/core/include/mp-units/framework/customization_points.h @@ -149,35 +149,16 @@ struct quantity_values { } }; -template -struct convert_explicitly { - using value_type = T; - T value; - // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - 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; - // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - constexpr explicit(false) convert_implicitly(T v) noexcept(std::is_nothrow_constructible_v) : value(std::move(v)) - { - } -}; - - /** * @brief Provides support for external quantity-like types * * The type trait should provide the @c reference object, a type alias @c rep, * 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. + * + * If the following expression is @c true, the specified conversion will be explicit: + * - @c explicit_import for the conversion from @c T to a @c quantity type, + * - @c explicit_export for the conversion from a @c quantity type to @c T. * * Usage example can be found in @c mp-units/systems/si/chrono.h header file. * @@ -193,8 +174,10 @@ struct quantity_like_traits; * a type alias @c rep, and static member functions @c to_numerical_value(T) that * returns the raw value of the the quantity being the offset of the point from the * origin and @c from_numerical_value(rep) that returns @c T formed this raw value. - * 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. + * + * If the following expression is @c true, the specified conversion will be explicit: + * - @c explicit_import for the conversion from @c T to a @c quantity_point type, + * - @c explicit_export for the conversion from a @c quantity_point type to @c T. * * Usage example can be found in @c mp-units/systems/si/chrono.h header file. * @@ -205,14 +188,4 @@ struct quantity_point_like_traits; MP_UNITS_EXPORT_END -namespace detail { - -template -concept ConversionSpec = is_specialization_of || is_specialization_of; - -template -concept ConversionSpecOf = ConversionSpec && std::same_as; - -} // namespace detail - } // namespace mp_units diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 4892dcb6..5892742a 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -198,12 +198,10 @@ public: template requires detail::QuantityConvertibleTo, quantity> - constexpr explicit(is_specialization_of::to_numerical_value(std::declval())), - convert_explicitly> || + constexpr explicit(quantity_like_traits::explicit_import || // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) !std::convertible_to::rep, Rep>) quantity(const Q& q) : - quantity( - ::mp_units::quantity{quantity_like_traits::to_numerical_value(q).value, quantity_like_traits::reference}) + quantity(::mp_units::quantity{quantity_like_traits::to_numerical_value(q), quantity_like_traits::reference}) { } @@ -309,9 +307,7 @@ public: template> requires detail::QuantityConvertibleTo> - [[nodiscard]] explicit(is_specialization_of::from_numerical_value( - numerical_value_is_an_implementation_detail_)), - convert_explicitly> || + [[nodiscard]] explicit(quantity_like_traits::explicit_export || !std::convertible_to::rep>) constexpr // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) operator Q_() const @@ -319,8 +315,7 @@ public: std::is_nothrow_copy_constructible_v) { return quantity_like_traits::from_numerical_value( - numerical_value_in(get_unit(quantity_like_traits::reference))) - .value; + numerical_value_in(get_unit(quantity_like_traits::reference))); } // member unary operators @@ -648,8 +643,7 @@ template explicit(false) quantity(Value) -> quantity; template -explicit( - is_specialization_of::to_numerical_value(std::declval())), convert_explicitly>) +explicit(quantity_like_traits::explicit_import) quantity(Q) -> quantity::reference, typename quantity_like_traits::rep>; MP_UNITS_EXPORT_END diff --git a/src/core/include/mp-units/framework/quantity_concepts.h b/src/core/include/mp-units/framework/quantity_concepts.h index 2cdf9eb7..5f08235d 100644 --- a/src/core/include/mp-units/framework/quantity_concepts.h +++ b/src/core/include/mp-units/framework/quantity_concepts.h @@ -45,23 +45,33 @@ constexpr bool is_derived_from_specialization_of_quantity = } // namespace detail -MP_UNITS_EXPORT_BEGIN - /** * @brief A concept matching all quantities in the library * * Satisfied by all types being a either specialization or derived from `quantity` */ -template +MP_UNITS_EXPORT template concept Quantity = detail::is_derived_from_specialization_of_quantity; +namespace detail { + +template typename Traits> +concept QuantityLikeImpl = requires(const T& qty, const Traits::rep& num) { + { Traits::to_numerical_value(qty) } -> std::same_as::rep>; + { Traits::from_numerical_value(num) } -> std::same_as; + { Traits::explicit_import } -> std::convertible_to; + { Traits::explicit_export } -> std::convertible_to; +}; + +} // namespace detail + /** * @brief A concept matching all quantities with provided quantity spec * * Satisfied by all quantities with a quantity_spec being the instantiation derived from * the provided quantity_spec type. */ -template +MP_UNITS_EXPORT template concept QuantityOf = Quantity && QuantitySpecOf, QS>; /** @@ -70,18 +80,9 @@ concept QuantityOf = Quantity && QuantitySpecOf -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) { - { quantity_like_traits::to_numerical_value(q) } -> detail::ConversionSpecOf::rep>; - { quantity_like_traits::from_numerical_value(v) } -> detail::ConversionSpecOf; +MP_UNITS_EXPORT template +concept QuantityLike = !Quantity && detail::QuantityLikeImpl && requires { + typename quantity::reference, typename quantity_like_traits::rep>; }; -MP_UNITS_EXPORT_END - } // namespace mp_units diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index ba312544..5cbd474b 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -248,13 +248,12 @@ public: quantity::reference, typename quantity_point_like_traits::rep>, quantity_type> constexpr explicit( - is_specialization_of::to_numerical_value(std::declval())), - convert_explicitly> || + quantity_point_like_traits::explicit_import || !std::convertible_to< quantity::reference, typename quantity_point_like_traits::rep>, quantity_type>) // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) quantity_point(const QP& qp) : - quantity_from_origin_is_an_implementation_detail_(quantity_point_like_traits::to_numerical_value(qp).value, + quantity_from_origin_is_an_implementation_detail_(quantity_point_like_traits::to_numerical_value(qp), get_unit(quantity_point_like_traits::reference)) { } @@ -371,10 +370,7 @@ public: std::convertible_to::reference, typename quantity_point_like_traits::rep>> [[nodiscard]] explicit( - is_specialization_of< - decltype(quantity_point_like_traits::from_numerical_value( - quantity_from_origin_is_an_implementation_detail_.numerical_value_is_an_implementation_detail_)), - convert_explicitly> || + quantity_point_like_traits::explicit_export || !std::convertible_to::reference, typename quantity_point_like_traits::rep>>) constexpr // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) @@ -384,8 +380,7 @@ public: std::is_nothrow_copy_constructible_v) { return quantity_point_like_traits::from_numerical_value( - quantity_from_origin_is_an_implementation_detail_.numerical_value_is_an_implementation_detail_) - .value; + quantity_from_origin_is_an_implementation_detail_.numerical_value_is_an_implementation_detail_); } template> @@ -393,10 +388,7 @@ public: std::convertible_to::reference, typename quantity_point_like_traits::rep>> [[nodiscard]] explicit( - is_specialization_of< - decltype(quantity_point_like_traits::from_numerical_value( - quantity_from_origin_is_an_implementation_detail_.numerical_value_is_an_implementation_detail_)), - convert_explicitly> || + quantity_point_like_traits::explicit_export || !std::convertible_to::reference, typename quantity_point_like_traits::rep>>) constexpr // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) @@ -406,8 +398,7 @@ public: std::is_nothrow_move_constructible_v) { return quantity_point_like_traits::from_numerical_value( - std::move(quantity_from_origin_is_an_implementation_detail_).numerical_value_is_an_implementation_detail_) - .value; + std::move(quantity_from_origin_is_an_implementation_detail_).numerical_value_is_an_implementation_detail_); } // member unary operators @@ -561,8 +552,7 @@ template PO> quantity_point(Q q, PO) -> quantity_point; template -explicit(is_specialization_of::to_numerical_value(std::declval())), - convert_explicitly>) quantity_point(QP) +explicit(quantity_point_like_traits::explicit_import) quantity_point(QP) -> quantity_point::reference, quantity_point_like_traits::point_origin, typename quantity_point_like_traits::rep>; 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 3e40b7df..ddd7cb6c 100644 --- a/src/core/include/mp-units/framework/quantity_point_concepts.h +++ b/src/core/include/mp-units/framework/quantity_point_concepts.h @@ -135,19 +135,9 @@ concept QuantityPointOf = * all quantity_point-specific information. */ MP_UNITS_EXPORT template -concept QuantityPointLike = requires { - quantity_point_like_traits::reference; - requires Reference::reference)>>; - quantity_point_like_traits::point_origin; - requires PointOrigin::point_origin)>>; - typename quantity_point_like_traits::rep; - requires RepresentationOf::rep, - get_quantity_spec(quantity_point_like_traits::reference).character>; -} && requires(T qp, typename quantity_point_like_traits::rep v) { - { - quantity_point_like_traits::to_numerical_value(qp) - } -> detail::ConversionSpecOf::rep>; - { quantity_point_like_traits::from_numerical_value(v) } -> detail::ConversionSpecOf; +concept QuantityPointLike = !QuantityPoint && detail::QuantityLikeImpl && requires { + typename quantity_point::reference, quantity_point_like_traits::point_origin, + typename quantity_point_like_traits::rep>; }; } // namespace mp_units diff --git a/src/systems/include/mp-units/systems/si/chrono.h b/src/systems/include/mp-units/systems/si/chrono.h index 86589107..b3c84990 100644 --- a/src/systems/include/mp-units/systems/si/chrono.h +++ b/src/systems/include/mp-units/systems/si/chrono.h @@ -73,18 +73,21 @@ template MP_UNITS_EXPORT template struct quantity_like_traits> { static constexpr auto reference = detail::time_unit_from_chrono_period(); + static constexpr bool explicit_import = false; + static constexpr bool explicit_export = false; using rep = Rep; + using T = std::chrono::duration; - [[nodiscard]] static constexpr convert_implicitly to_numerical_value( - const std::chrono::duration& q) noexcept(std::is_nothrow_copy_constructible_v) + [[nodiscard]] static constexpr rep to_numerical_value(const T& 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) + [[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept( + std::is_nothrow_copy_constructible_v) { - return std::chrono::duration(v); + return T(v); } }; @@ -99,18 +102,19 @@ MP_UNITS_EXPORT_BEGIN 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; + static constexpr bool explicit_import = false; + static constexpr bool explicit_export = false; using rep = Rep; + using T = std::chrono::time_point>; - [[nodiscard]] static constexpr convert_implicitly to_numerical_value(const T& tp) noexcept( - std::is_nothrow_copy_constructible_v) + [[nodiscard]] static constexpr rep to_numerical_value(const T& tp) noexcept(std::is_nothrow_copy_constructible_v) { return tp.time_since_epoch().count(); } - [[nodiscard]] static constexpr convert_implicitly from_numerical_value(const rep& v) noexcept( + [[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept( std::is_nothrow_copy_constructible_v) { return T(std::chrono::duration(v));