diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ecaab91b..234d1225 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,7 +3,7 @@ - **0.7.0 WIP** - (!) refactor: `ScalableNumber` renamed to `QuantityValue` - (!) refactor: output stream operators moved to the `units/quantity_io.h` header file - - refactor: `quantity_point` (partially) updated to reflect latest changes to `quantity` + - refactor: quantity (kind) point updated to reflect latest changes to `quantity` - refactor: basic concepts, `quantity` and `quantity_cast` refactored - refactor: `abs()` definition refactored to be more explicit about the return type - feat: quantity (point) kind support added (thanks [@johelegp](https://github.com/johelegp)) diff --git a/docs/reference/core/customization_points.rst b/docs/reference/core/customization_points.rst index cb80f132..f92fd8df 100644 --- a/docs/reference/core/customization_points.rst +++ b/docs/reference/core/customization_points.rst @@ -8,3 +8,6 @@ Customization Points .. doxygenstruct:: units::quantity_like_traits :members: + +.. doxygenstruct:: units::quantity_point_like_traits + :members: diff --git a/docs/use_cases/interoperability.rst b/docs/use_cases/interoperability.rst index 675ab5f7..36569007 100644 --- a/docs/use_cases/interoperability.rst +++ b/docs/use_cases/interoperability.rst @@ -53,3 +53,14 @@ such an explicit conversion:: using namespace std::chrono_literals; quantity q = 1s; // ERROR + +For external quantity point-like types, `quantity_point_like_traits` is also provided. +It works just like `quantity_like_traits`, except that +``count(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value. + +Similar to `quantity` and `quantity_kind`, `quantity_point` and `quantity_kind_point` +provide a deduction guide from `QuantityPointLike`:: + + using namespace std::chrono_literals; + + static_assert(quantity_point{std::chrono::sys_seconds{1s}} + 1_q_s == quantity_point{2s}); diff --git a/src/include/units/bits/basic_concepts.h b/src/include/units/bits/basic_concepts.h index a6cb8569..1dfb4f32 100644 --- a/src/include/units/bits/basic_concepts.h +++ b/src/include/units/bits/basic_concepts.h @@ -264,6 +264,9 @@ inline constexpr bool is_quantity_point_kind = false; template inline constexpr bool is_quantity_like = false; +template +inline constexpr bool is_quantity_point_like = false; + } // namespace detail /** @@ -298,7 +301,7 @@ concept QuantityKind = detail::is_quantity_kind; template concept QuantityPointKind = detail::is_quantity_point_kind; -// QuantityLike +// QuantityLike, QuantityPointLike /** * @brief A concept matching all quantity-like types (other than specialization of @c quantity) @@ -309,6 +312,15 @@ concept QuantityPointKind = detail::is_quantity_point_kind; template concept QuantityLike = detail::is_quantity_like; +/** + * @brief A concept matching all quantity point-like types (other than specialization of @c quantity_point) + * + * Satisfied by all types for which a correct specialization of `quantity_point_like_traits` + * type trait is provided. + */ +template +concept QuantityPointLike = detail::is_quantity_point_like; + // QuantityValue template @@ -390,6 +402,18 @@ template } inline constexpr bool is_quantity_like = true; +template + requires requires(T q) { + typename quantity_point_like_traits::dimension; + typename quantity_point_like_traits::unit; + typename quantity_point_like_traits::rep; + requires Dimension::dimension>; + requires Unit::unit>; + requires QuantityValue::rep>; + { quantity_point_like_traits::relative(q) } -> QuantityLike; + } +inline constexpr bool is_quantity_point_like = true; + } // namespace detail } // namespace units diff --git a/src/include/units/chrono.h b/src/include/units/chrono.h index 84d31c26..8a057c29 100644 --- a/src/include/units/chrono.h +++ b/src/include/units/chrono.h @@ -36,4 +36,15 @@ struct quantity_like_traits> { [[nodiscard]] static constexpr rep count(const std::chrono::duration& q) { return q.count(); } }; +template +struct quantity_point_like_traits>> { + using dimension = physical::si::dim_time; + using unit = downcast_unit; + using rep = Rep; + [[nodiscard]] static constexpr auto relative( + const std::chrono::time_point>& qp) { + return qp.time_since_epoch(); + } +}; + } // namespace units diff --git a/src/include/units/customization_points.h b/src/include/units/customization_points.h index 43feba27..1cb1bb64 100644 --- a/src/include/units/customization_points.h +++ b/src/include/units/customization_points.h @@ -85,4 +85,17 @@ struct quantity_values { template struct quantity_like_traits; +/** + * @brief Provides support for external quantity point-like types + * + * The type trait should provide the following nested type aliases: @c dimension, @c unit, @c rep, + * and a static member function @c relative(T) that will return the quantity-like value of the quantity point. + * + * Usage example can be found in @c units/chrono.h header file. + * + * @tparam T the type to provide support for + */ +template +struct quantity_point_like_traits; + } // namespace units diff --git a/src/include/units/quantity_point.h b/src/include/units/quantity_point.h index a2506956..0d236652 100644 --- a/src/include/units/quantity_point.h +++ b/src/include/units/quantity_point.h @@ -28,6 +28,10 @@ namespace units { +template +using quantity_point_like_type = quantity_point::dimension, + typename quantity_point_like_traits::unit, typename quantity_point_like_traits::rep>; + /** * @brief A quantity point * @@ -63,6 +67,11 @@ public: requires std::is_constructible_v constexpr explicit quantity_point(const Q& q) : q_{q} {} + template + constexpr explicit quantity_point(const QP& qp) + requires std::is_constructible_v::relative(qp))> + : q_{quantity_point_like_traits::relative(qp)} {} + template requires std::is_convertible_v constexpr quantity_point(const QP2& qp) : q_{qp.relative()} {} @@ -182,6 +191,9 @@ template quantity_point(Q) -> quantity_point::dimension, typename quantity_like_traits::unit, typename quantity_like_traits::rep>; +template +explicit quantity_point(QP) -> quantity_point_like_type; + namespace detail { template diff --git a/src/include/units/quantity_point_kind.h b/src/include/units/quantity_point_kind.h index 83cd8f4c..94847c7e 100644 --- a/src/include/units/quantity_point_kind.h +++ b/src/include/units/quantity_point_kind.h @@ -67,6 +67,10 @@ public: requires std::is_constructible_v constexpr explicit quantity_point_kind(const Q& q) : qk_{q} {} + template + requires std::is_constructible_v, QP> + constexpr explicit quantity_point_kind(const QP& qp) : qk_{quantity_point_like_traits::relative(qp)} {} + constexpr explicit quantity_point_kind(const quantity_point& qp) : qk_{qp.relative()} {} constexpr explicit quantity_point_kind(const quantity_kind_type& qk) : qk_{qk} {} diff --git a/test/unit_test/static/chrono_test.cpp b/test/unit_test/static/chrono_test.cpp index 61d80d3f..7359a94f 100644 --- a/test/unit_test/static/chrono_test.cpp +++ b/test/unit_test/static/chrono_test.cpp @@ -23,6 +23,7 @@ #include "test_tools.h" #include #include +#include namespace { @@ -30,8 +31,13 @@ using namespace units; using namespace units::physical; using namespace units::physical::si::literals; using namespace std::chrono_literals; +using sys_seconds = std::chrono::time_point; +using sys_days = std::chrono::time_point, std::chrono::hours::period>>>; +template using time_point = quantity_point; static_assert(QuantityLike); +static_assert(QuantityPointLike); // construction - same rep type static_assert(std::constructible_from, std::chrono::seconds>); @@ -42,6 +48,14 @@ static_assert(std::constructible_from>); static_assert(!std::constructible_from, std::chrono::seconds>); static_assert(!std::convertible_to>); +static_assert(std::constructible_from, sys_seconds>); +static_assert(!std::convertible_to>); +static_assert(std::constructible_from, sys_days>); +static_assert(!std::convertible_to>); +static_assert(std::constructible_from, sys_days>); +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>); @@ -50,15 +64,25 @@ static_assert(std::constructible_from, std::chrono::hours>) 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::convertible_to>); +static_assert(std::constructible_from, sys_days>); +static_assert(!std::convertible_to>); +static_assert(std::constructible_from, sys_seconds>); +static_assert(!std::convertible_to>); // CTAD static_assert(is_same_v>); static_assert(is_same_v>); +static_assert(is_same_v>); +static_assert(is_same_v>); // operators static_assert(quantity{1s} + 1_q_s == 2_q_s); static_assert(quantity{1s} + 1_q_min == 61_q_s); static_assert(10_q_m / quantity{2s} == 5_q_m_per_s); +static_assert(quantity_point{sys_seconds{1s}} + 1_q_s == quantity_point{2_q_s}); +static_assert(quantity_point{sys_seconds{1s}} + 1_q_min == quantity_point{61_q_s}); } // namespace diff --git a/test/unit_test/static/quantity_point_kind_test.cpp b/test/unit_test/static/quantity_point_kind_test.cpp index 7c993901..b5eba3d0 100644 --- a/test/unit_test/static/quantity_point_kind_test.cpp +++ b/test/unit_test/static/quantity_point_kind_test.cpp @@ -39,6 +39,7 @@ using namespace units; namespace si = physical::si; using namespace si; using namespace unit_constants; +using sys_seconds = std::chrono::time_point; constexpr auto cgs_cm = cgs::unit_constants::cm; @@ -303,6 +304,8 @@ static_assert(!constructible_or_convertible_from>(quantity_p static_assert(!constructible_or_convertible_from>(quantity_point(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(quantity_point(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * s))); + +static_assert(construct_from_only>(sys_seconds{42s}).relative().common() == 42 * s); // clang-format on diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 5acf737d..1c0815c6 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -36,6 +36,7 @@ using namespace units; using namespace physical::si; using namespace unit_constants; using namespace std::chrono_literals; +using sys_seconds = std::chrono::time_point; // class invariants @@ -66,7 +67,9 @@ static_assert(quantity_point(1).relative() == quantity(1)); static_assert(!std::is_convertible_v>); static_assert(quantity_point(42s).relative() == 42 * s); +static_assert(quantity_point(sys_seconds{42s}).relative() == 42 * s); static_assert(!std::is_convertible_v>); +static_assert(!std::is_convertible_v>); static_assert(quantity_point().relative() == 0_q_m); constexpr quantity_point km{1000_q_m};