diff --git a/.clang-format b/.clang-format index 7abab46799..756b6fdc9d 100644 --- a/.clang-format +++ b/.clang-format @@ -63,7 +63,7 @@ CommentPragmas: '^ NOLINT' # CompactNamespaces: false # ConstructorInitializerAllOnOneLineOrOnePerLine: true # ConstructorInitializerIndentWidth: 4 -# ContinuationIndentWidth: 4 +ContinuationIndentWidth: 2 # Cpp11BracedListStyle: true DeriveLineEnding: false DerivePointerAlignment: false @@ -109,7 +109,7 @@ IndentRequires: true # KeepEmptyLinesAtTheStartOfBlocks: false # MacroBlockBegin: '' # MacroBlockEnd: '' -# MaxEmptyLinesToKeep: 1 +MaxEmptyLinesToKeep: 2 # NamespaceIndentation: None # ObjCBinPackProtocolList: Never # ObjCBlockIndentWidth: 2 diff --git a/docs/_static/img/concepts.png b/docs/_static/img/concepts.png index 376773106c..001f4ee095 100644 Binary files a/docs/_static/img/concepts.png and b/docs/_static/img/concepts.png differ diff --git a/docs/framework/basic_concepts.rst b/docs/framework/basic_concepts.rst index ddff346128..2151d4a74c 100644 --- a/docs/framework/basic_concepts.rst +++ b/docs/framework/basic_concepts.rst @@ -23,7 +23,7 @@ The most important concepts in the library are `Unit`, `Dimension`, ] [QuantityPoint| - [quantity_point] + [quantity_point] ] [QuantityKind| @@ -39,13 +39,17 @@ The most important concepts in the library are `Unit`, `Dimension`, [Unit]<-[Quantity] [Quantity]<-[QuantityPoint] + [PointOrigin]<-[QuantityPoint] + [Dimension]<-[PointOrigin] + [PointOrigin]<-[PointKind] + [Kind]<-[QuantityKind] [Dimension]<-[Kind] [Quantity]<-[QuantityKind] [PointKind]<-[QuantityPointKind] [Kind]<-[PointKind] - [QuantityKind]<-[QuantityPointKind] + [QuantityKind]-<[QuantityPointKind] `Unit` is a basic building block of the library. Every dimension works with a concrete hierarchy of units. Such hierarchy defines a reference unit and @@ -60,7 +64,7 @@ derived dimensions. Examples: ``si::dim_time``, ``si::dim_length``, ``si::dim_sp specific representation. Examples: ``quantity``, ``si::length``, ``si::speed``. -`QuantityPoint` is an absolute `Quantity` with respect to some origin. +`QuantityPoint` is an absolute `Quantity` with respect to an origin. Examples: timestamp (as opposed to duration), absolute temperature (as opposed to temperature difference). @@ -68,6 +72,6 @@ Examples: timestamp (as opposed to duration), absolute temperature distance (``horizonal_kind``) and height (``vertical_kind``) are different kinds of a length quantity. -`QuantityPointKind` is an absolute `QuantityKind` with respect to some origin. +`QuantityPointKind` is an absolute `QuantityKind` with respect to an origin. Examples: altitude is a quantity point of ``vertical_kind`` (as opposed to height). diff --git a/docs/framework/dimensions.rst b/docs/framework/dimensions.rst index 55cec05413..4a8ab10a8d 100644 --- a/docs/framework/dimensions.rst +++ b/docs/framework/dimensions.rst @@ -106,8 +106,9 @@ Quantity Points +++++++++++++++ Quantity points have a more restricted set of operations. -Quantity can be added to or subtracted from a quantity point. -The result will always be a quantity point of the same dimension: +Quantity can be added to or subtracted +from a quantity point of the same origin. +The result will always be a quantity point of the same origin: .. code-block:: :emphasize-lines: 3-5 @@ -132,9 +133,10 @@ The result is a relative quantity of the same dimension: It is not allowed to: - - add quantity points - - subtract a quantity point from a quantity: - - multiply nor divide quantity points with anything else. + - add quantity points, + - subtract a quantity point from a quantity, + - multiply nor divide quantity points with anything else, and + - mix quantity points with different origins: .. code-block:: :emphasize-lines: 3-5 @@ -144,6 +146,8 @@ The result is a relative quantity of the same dimension: auto res1 = quantity_point{dist1} + quantity_point{dist2}; // ERROR auto res2 = dist1 - quantity_point{dist2}; // ERROR auto res3 = quantity_point{dist1} / (2 * s); // ERROR + auto res4 = quantity_point{std::chrono::utc_second{1s}} + + quantity_point{std::chrono::sys_second{1s}}; // ERROR Quantity Point Kinds ++++++++++++++++++++ diff --git a/docs/framework/quantity_points.rst b/docs/framework/quantity_points.rst index 9fd23af16a..a13ee71bf0 100644 --- a/docs/framework/quantity_points.rst +++ b/docs/framework/quantity_points.rst @@ -3,9 +3,20 @@ Quantity Points =============== -A quantity point is an absolute quantity with respect to zero -(which represents some origin) and is represented in the library with a -`quantity_point` class template. +A quantity point is an absolute quantity with respect to an origin +and is represented in the library with a `quantity_point` class template. + +Point Origins +------------- + +We need a `point_origin` to represent the origin of a quantity point:: + + struct mean_sea_level : point_origin {}; + +Quantities points with this origin represent a point from the mean sea level. + +The library offers a `dynamic_origin` +for quantity points whose origin is not specified in the type system. Construction @@ -14,7 +25,7 @@ Construction To create the quantity point object from a `quantity` we just have to pass the value to the `quantity_point` class template explicit constructor:: - quantity_point d(123 * km); + quantity_point, si::kilometre, double> d(123 * km); .. note:: @@ -25,7 +36,7 @@ the value to the `quantity_point` class template explicit constructor:: `copy initialization `_ **does not compile**:: - quantity_point d = 123 * km; // ERROR + quantity_point, si::kilometre, double> d = 123 * km; // ERROR Differences To Quantity diff --git a/docs/use_cases/interoperability.rst b/docs/use_cases/interoperability.rst index c5cf5fed7d..1fd7685cc0 100644 --- a/docs/use_cases/interoperability.rst +++ b/docs/use_cases/interoperability.rst @@ -56,11 +56,12 @@ such an explicit conversion:: For external quantity point-like types, `quantity_point_like_traits` is also provided. It works just like `quantity_like_traits`, except that -``number(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value. +``number(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value +and ``dimension`` is replaced with ``origin``. 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 * s == quantity_point{2s}); + static_assert((quantity_point{std::chrono::sys_seconds{1s}} + 1 * s).relative() == 2s); diff --git a/example/glide_computer/include/glide_computer.h b/example/glide_computer/include/glide_computer.h index 8185f95f89..ef9631f70b 100644 --- a/example/glide_computer/include/glide_computer.h +++ b/example/glide_computer/include/glide_computer.h @@ -30,6 +30,7 @@ #include // IWYU pragma: end_exports +#include #include #include // IWYU pragma: keep #include @@ -88,7 +89,7 @@ using altitude = units::quantity_point_kind; -using timestamp = units::quantity_point; +using timestamp = units::quantity_point, units::isq::si::second>; // speed using velocity = units::quantity_kind; diff --git a/src/core/include/units/bits/basic_concepts.h b/src/core/include/units/bits/basic_concepts.h index 4ec561150d..7b68b99401 100644 --- a/src/core/include/units/bits/basic_concepts.h +++ b/src/core/include/units/bits/basic_concepts.h @@ -205,6 +205,52 @@ concept UnitOf = Dimension && std::same_as::reference>; +// PointOrigin + +template +struct point_origin; + +/** + * @brief A concept matching a point origin + * + * Satisfied by types derived from an specialization of @c point_origin. + */ +template +concept PointOrigin = is_derived_from_specialization_of && + requires { + typename T::dimension; + requires Dimension; + typename T::point_origin; + requires std::same_as>; + requires !std::same_as>; + }; + +// RebindablePointOriginFor + +namespace detail { + +template +struct rebind_point_origin_dimension_impl { + using type = typename O::template rebind; +}; + +} // namespace detail + +template +using rebind_point_origin_dimension = typename conditional, std::type_identity, + detail::rebind_point_origin_dimension_impl>::type; + +/** + * @brief A concept predicating the possibility of changing an origin's dimension + * + * Satisfied by point origins whose dimension can be made to be `D`. + */ +template +concept RebindablePointOriginFor = + requires { typename rebind_point_origin_dimension; } && + PointOrigin> && + std::same_as::dimension>; + // Kind namespace detail { @@ -216,7 +262,7 @@ struct _kind_base; template typename Base> concept kind_impl_ = is_derived_from_specialization_of && - requires(T* t) { + requires { typename T::base_kind; typename T::dimension; requires Dimension; @@ -236,7 +282,7 @@ concept Kind = // PointKind namespace detail { -template +template struct _point_kind_base; } // namespace detail @@ -247,7 +293,12 @@ struct _point_kind_base; * Satisfied by all point kind types derived from an specialization of @c point_kind. */ template -concept PointKind = kind_impl_; +concept PointKind = + kind_impl_ && + requires { typename T::origin; } && + PointOrigin && + std::same_as && + std::same_as; // Reference namespace detail { diff --git a/src/core/include/units/bits/common_quantity.h b/src/core/include/units/bits/common_quantity.h index 1bcc0cfb60..789ff5b132 100644 --- a/src/core/include/units/bits/common_quantity.h +++ b/src/core/include/units/bits/common_quantity.h @@ -31,7 +31,7 @@ namespace units { template U, Representation Rep> class quantity; -template U, Representation Rep> +template U, Representation Rep> class quantity_point; template U, Representation Rep> @@ -99,7 +99,8 @@ template QP2> requires requires { typename common_type_t; } struct common_type { using type = units::quantity_point< - typename common_type_t::dimension, + units::rebind_point_origin_dimension::dimension>, typename common_type_t::unit, typename common_type_t::rep>; }; diff --git a/src/core/include/units/bits/equivalent.h b/src/core/include/units/bits/equivalent.h index 8082587069..dca2d65f3a 100644 --- a/src/core/include/units/bits/equivalent.h +++ b/src/core/include/units/bits/equivalent.h @@ -81,29 +81,48 @@ template struct equivalent_unit : std::disjunction, std::bool_constant::ratio == U2::ratio / dimension_unit::ratio>> {}; +// point origins + +template +struct equivalent_impl : std::bool_constant && RebindablePointOriginFor && + std::same_as> && + std::same_as>; + } && equivalent_impl::value> {}; + // (point) kinds -template - requires (Kind && Kind) || (PointKind && PointKind) +template struct equivalent_impl : std::conjunction, equivalent_impl> {}; +template +struct equivalent_impl : + std::conjunction, + equivalent_impl> {}; + // quantities, quantity points, quantity (point) kinds -template - requires (Quantity && Quantity) || (QuantityPoint && QuantityPoint) +template struct equivalent_impl : std::conjunction, equivalent_unit> {}; -template - requires (QuantityKind && QuantityKind) || (QuantityPointKind && QuantityPointKind) +template +struct equivalent_impl : std::conjunction, + equivalent_impl> {}; + +template struct equivalent_impl : std::conjunction, equivalent_impl> {}; +template +struct equivalent_impl : std::conjunction, + equivalent_impl> {}; + } // namespace detail template diff --git a/src/core/include/units/bits/quantity_of.h b/src/core/include/units/bits/quantity_of.h index 832f273f43..4ec1368fb7 100644 --- a/src/core/include/units/bits/quantity_of.h +++ b/src/core/include/units/bits/quantity_of.h @@ -85,21 +85,20 @@ template concept QuantityEquivalentTo = Quantity && QuantityOf; /** - * @brief A concept matching all quantity points with provided dimension + * @brief A concept matching all quantity points of the provided origin * - * Satisfied by all quantity points with a dimension being the instantiation derived from - * the provided dimension type. + * Satisfied by all quantity points with an origin equivalent to the provided one. */ -template -concept QuantityPointOf = QuantityPoint && Dimension && equivalent; +template +concept QuantityPointOf = QuantityPoint && PointOrigin && equivalent; /** * @brief A concept matching two equivalent quantity points * - * Satisfied by quantity points having equivalent dimensions. + * Satisfied by quantity points having equivalent origins. */ template -concept QuantityPointEquivalentTo = QuantityPoint && QuantityPointOf; +concept QuantityPointEquivalentTo = QuantityPoint && QuantityPointOf; /** * @brief A concept matching only quantity kinds of a specific kind. diff --git a/src/core/include/units/chrono.h b/src/core/include/units/chrono.h index 5e5d14e9f9..d85496bdbc 100644 --- a/src/core/include/units/chrono.h +++ b/src/core/include/units/chrono.h @@ -25,6 +25,7 @@ #include // IWYU pragma: begin_exports #include +#include #include // IWYU pragma: end_exports @@ -38,10 +39,13 @@ struct quantity_like_traits> { [[nodiscard]] static constexpr rep number(const std::chrono::duration& q) { return q.count(); } }; +template +struct clock_origin : point_origin { }; + template struct quantity_point_like_traits>> { - using dimension = isq::si::dim_time; - using unit = downcast_unit; + using origin = clock_origin; + using unit = downcast_unit; using rep = Rep; [[nodiscard]] static constexpr auto relative( const std::chrono::time_point>& qp) { @@ -49,4 +53,11 @@ struct quantity_point_like_traits +inline constexpr bool is_quantity_point_like>> = true; + +} // namespace detail + } // namespace units diff --git a/src/core/include/units/customization_points.h b/src/core/include/units/customization_points.h index 5b3a3d56a8..04827e1f3d 100644 --- a/src/core/include/units/customization_points.h +++ b/src/core/include/units/customization_points.h @@ -88,7 +88,7 @@ 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, + * The type trait should provide the following nested type aliases: @c origin, @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. diff --git a/src/core/include/units/kind.h b/src/core/include/units/kind.h index 5494adc52f..690dcbd5ca 100644 --- a/src/core/include/units/kind.h +++ b/src/core/include/units/kind.h @@ -27,6 +27,9 @@ namespace units { +template +struct dynamic_origin; + namespace detail { template @@ -35,10 +38,11 @@ struct _kind_base : downcast_base<_kind_base> { using dimension = D; }; -template -struct _point_kind_base : downcast_base<_point_kind_base> { +template +struct _point_kind_base : downcast_base<_point_kind_base> { using base_kind = K; using dimension = typename K::dimension; + using origin = O; }; } // namespace detail @@ -47,9 +51,9 @@ template requires Kind>> using downcast_kind = downcast>; -template - requires PointKind>> -using downcast_point_kind = downcast>; +template> + requires PointKind>> +using downcast_point_kind = downcast>; template struct kind : downcast_dispatch> {}; @@ -58,7 +62,7 @@ template requires std::same_as struct derived_kind : downcast_dispatch> {}; -template -struct point_kind : downcast_dispatch> {}; +template> +struct point_kind : downcast_dispatch> {}; } // namespace units diff --git a/src/core/include/units/point_origin.h b/src/core/include/units/point_origin.h new file mode 100644 index 0000000000..581ccbfc8f --- /dev/null +++ b/src/core/include/units/point_origin.h @@ -0,0 +1,34 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace units { + +template +struct point_origin { + using dimension = D; +}; + +} // namespace units diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index 655a07208f..1472b7012f 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -39,7 +39,7 @@ namespace units { template U, Representation Rep> class quantity; -template U, Representation Rep> +template U, Representation Rep> class quantity_point; template U, Representation Rep> @@ -234,10 +234,12 @@ template Rep * * @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast */ -template - requires is_specialization_of || - requires(quantity q) { quantity_cast(q); } -[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& qp) +template +[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& qp) + requires requires { requires is_specialization_of; + requires requires { quantity_cast(qp.relative()); }; + requires equivalent; } || // TODO: Simplify when Clang catches up. + requires { quantity_cast(qp.relative()); } { if constexpr (is_specialization_of) return quantity_point(quantity_cast(qp.relative())); @@ -261,11 +263,11 @@ template * @tparam ToD a dimension type to use for a target quantity * @tparam ToU a unit type to use for a target quantity */ -template - requires equivalent && UnitOf -[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& q) +template + requires equivalent && UnitOf && RebindablePointOriginFor +[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& q) { - return quantity_point_cast>(q); + return quantity_point_cast, ToU, Rep>>(q); } /** @@ -347,7 +349,8 @@ template template [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind& qpk) requires requires { requires is_specialization_of; - requires requires { quantity_kind_cast(qpk.relative()); }; } || + requires requires { quantity_kind_cast(qpk.relative()); }; + requires equivalent; } || requires { requires PointKind && UnitOf; } || requires { quantity_kind_cast(qpk.relative()); } // TODO: Simplify when Clang catches up. { diff --git a/src/core/include/units/quantity_point.h b/src/core/include/units/quantity_point.h index cbb5e2a75a..4da06901ac 100644 --- a/src/core/include/units/quantity_point.h +++ b/src/core/include/units/quantity_point.h @@ -24,6 +24,7 @@ #pragma once // IWYU pragma: begin_exports +#include #include #include // IWYU pragma: end_exports @@ -34,18 +35,32 @@ namespace units { /** - * @brief A quantity point + * @brief A statically unspecified quantity point origin * - * An absolute quantity with respect to zero (which represents some origin). + * An origin, unspecified in the type system, from which an absolute quantity is measured from. * * @tparam D a dimension of the quantity point (can be either a BaseDimension or a DerivedDimension) + */ +template +struct dynamic_origin : point_origin { + template + using rebind = dynamic_origin; +}; + +/** + * @brief A quantity point + * + * An absolute quantity measured from an origin. + * + * @tparam O a type that represents the origin from which the quantity point is measured from * @tparam U a measurement unit of the quantity point * @tparam Rep a type to be used to represent values of a quantity point */ -template U, Representation Rep = double> +template U, Representation Rep = double> class quantity_point { public: - using quantity_type = quantity; + using origin = O; + using quantity_type = quantity; using dimension = typename quantity_type::dimension; using unit = typename quantity_type::unit; using rep = typename quantity_type::rep; @@ -63,13 +78,14 @@ class quantity_point { requires std::constructible_from constexpr explicit quantity_point(T&& t) : q_(std::forward(t)) {} - template + template QP2> requires std::convertible_to constexpr explicit(false) quantity_point(const QP2& qp) : q_(qp.relative()) {} template constexpr explicit quantity_point(const QP& qp) - requires std::is_constructible_v::relative(qp))> + requires std::is_constructible_v::relative(qp))> && + equivalent::origin> : q_(quantity_point_like_traits::relative(qp)) {} quantity_point& operator=(const quantity_point&) = default; @@ -141,7 +157,8 @@ class quantity_point { { const auto q = lhs.relative() + rhs; using q_type = decltype(q); - return quantity_point(q); + return quantity_point, typename q_type::unit, + typename q_type::rep>(q); } template @@ -157,24 +174,25 @@ class quantity_point { { const auto q = lhs.relative() - rhs; using q_type = decltype(q); - return quantity_point(q); + return quantity_point, typename q_type::unit, + typename q_type::rep>(q); } - template + template QP> [[nodiscard]] friend constexpr Quantity auto operator-(const quantity_point& lhs, const QP& rhs) requires requires(quantity_type q) { q - rhs.relative(); } { return lhs.relative() - rhs.relative(); } - template + template QP> requires std::three_way_comparable_with [[nodiscard]] friend constexpr auto operator<=>(const quantity_point& lhs, const QP& rhs) { return lhs.relative() <=> rhs.relative(); } - template + template QP> requires std::equality_comparable_with [[nodiscard]] friend constexpr bool operator==(const quantity_point& lhs, const QP& rhs) { @@ -183,24 +201,24 @@ class quantity_point { }; template -explicit quantity_point(Rep) -> quantity_point; +explicit quantity_point(Rep) -> quantity_point, one, Rep>; template -explicit quantity_point(Q) -> quantity_point; +explicit quantity_point(Q) -> quantity_point, typename Q::unit, typename Q::rep>; template -explicit quantity_point(Q) -> quantity_point::dimension, +explicit quantity_point(Q) -> quantity_point::dimension>, typename quantity_like_traits::unit, typename quantity_like_traits::rep>; template -explicit quantity_point(QP) -> quantity_point::dimension, +explicit quantity_point(QP) -> quantity_point::origin, typename quantity_point_like_traits::unit, typename quantity_point_like_traits::rep>; namespace detail { -template -inline constexpr bool is_quantity_point> = true; +template +inline constexpr bool is_quantity_point> = true; } // namespace detail diff --git a/src/core/include/units/quantity_point_kind.h b/src/core/include/units/quantity_point_kind.h index 213de44773..a2c5f1a15b 100644 --- a/src/core/include/units/quantity_point_kind.h +++ b/src/core/include/units/quantity_point_kind.h @@ -34,7 +34,7 @@ namespace units { /** * @brief A quantity point kind * - * An absolute quantity kind with respect to zero (which represents some origin). + * An absolute quantity kind measured from an origin. * * @tparam PK the point kind of quantity point * @tparam U the measurement unit of the quantity point kind @@ -45,6 +45,7 @@ class quantity_point_kind { public: using point_kind_type = PK; using kind_type = typename PK::base_kind; + using origin = typename point_kind_type::origin; using quantity_kind_type = quantity_kind; using quantity_type = typename quantity_kind_type::quantity_type; using dimension = typename quantity_type::dimension; @@ -64,14 +65,14 @@ class quantity_point_kind { requires std::constructible_from constexpr explicit quantity_point_kind(T&& t) : qk_(std::forward(t)) {} - constexpr explicit quantity_point_kind(const quantity_point& qp) : qk_(qp.relative()) {} - constexpr explicit quantity_point_kind(quantity_point&& qp) : qk_(std::move(qp).relative()) {} + constexpr explicit quantity_point_kind(const quantity_point& qp) : qk_(qp.relative()) {} + constexpr explicit quantity_point_kind(quantity_point&& qp) : qk_(std::move(qp).relative()) {} template - requires std::constructible_from, QP> + requires std::constructible_from, QP> constexpr explicit quantity_point_kind(const QP& qp) : qk_(quantity_point_like_traits::relative(qp)) {} - template QPK2> + template QPK2> requires std::convertible_to constexpr explicit(false) quantity_point_kind(const QPK2& qpk) : qk_(qpk.relative()) {} @@ -159,20 +160,21 @@ class quantity_point_kind { return units::quantity_point_kind(lhs.relative() - rhs); } - [[nodiscard]] friend constexpr QuantityKind auto operator-(const quantity_point_kind& lhs, const quantity_point_kind& rhs) + template QPK> + [[nodiscard]] friend constexpr QuantityKind auto operator-(const quantity_point_kind& lhs, const QPK& rhs) requires requires(quantity_kind_type qk) { qk - qk; } { return lhs.relative() - rhs.relative(); } - template + template QPK> requires std::three_way_comparable_with [[nodiscard]] friend constexpr auto operator<=>(const quantity_point_kind& lhs, const QPK& rhs) { return lhs.relative() <=> rhs.relative(); } - template + template QPK> requires std::equality_comparable_with [[nodiscard]] friend constexpr bool operator==(const quantity_point_kind& lhs, const QPK& rhs) { diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index f3981ce48d..0c5b3d9c80 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -51,6 +51,7 @@ add_library(unit_tests_static iec80000_test.cpp kind_test.cpp math_test.cpp + point_origin_test.cpp ratio_test.cpp references_test.cpp si_test.cpp diff --git a/test/unit_test/static/chrono_test.cpp b/test/unit_test/static/chrono_test.cpp index ec5567234d..ed812548e5 100644 --- a/test/unit_test/static/chrono_test.cpp +++ b/test/unit_test/static/chrono_test.cpp @@ -36,7 +36,7 @@ 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; +template using time_point = quantity_point, U, Rep>; static_assert(QuantityLike); static_assert(QuantityPointLike); @@ -50,14 +50,17 @@ 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>); +static_assert(std::constructible_from, sys_seconds>); +static_assert(!std::constructible_from, sys_seconds>); +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::constructible_from, sys_days>); +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>); @@ -66,25 +69,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>); +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>); +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}); +static_assert(quantity_point{sys_seconds{1s}} + 1_q_s == time_point{2_q_s}); +static_assert(quantity_point{sys_seconds{1s}} + 1_q_min == time_point{61_q_s}); } // namespace diff --git a/test/unit_test/static/concepts_test.cpp b/test/unit_test/static/concepts_test.cpp index fd0ce1f211..a54e3cd5f0 100644 --- a/test/unit_test/static/concepts_test.cpp +++ b/test/unit_test/static/concepts_test.cpp @@ -27,7 +27,7 @@ #include // IWYU pragma: keep #include #include -#include +#include #include #include #include @@ -112,11 +112,11 @@ static_assert(!Representation); static_assert(Quantity>); static_assert(!Quantity); -static_assert(!Quantity>); +static_assert(!Quantity, si::metre>>); // QuantityPoint -static_assert(QuantityPoint>); +static_assert(QuantityPoint, si::metre>>); static_assert(!QuantityPoint>); static_assert(!QuantityPoint); @@ -141,4 +141,9 @@ static_assert(QuantityOf, si::dim_length>); static_assert(QuantityOf, si::fps::dim_length>); static_assert(!QuantityOf, si::dim_time>); +static_assert(QuantityPointOf, si::second, int>, dynamic_origin>); +static_assert(QuantityPointOf, si::second, int>, clock_origin>); +static_assert(!QuantityPointOf, si::second, int>, clock_origin>); +static_assert(!QuantityPointOf, si::second, int>, dynamic_origin>); + } // namespace diff --git a/test/unit_test/static/kind_test.cpp b/test/unit_test/static/kind_test.cpp index fb6ba0bc86..3e9b4eb8fa 100644 --- a/test/unit_test/static/kind_test.cpp +++ b/test/unit_test/static/kind_test.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include using namespace units; @@ -97,7 +98,6 @@ static_assert(PointKind); static_assert(is_same_v); static_assert(is_same_v); -static_assert(is_same_v>); static_assert(is_same_v>); static_assert(equivalent); @@ -137,7 +137,6 @@ static_assert(PointKind); static_assert(is_same_v); static_assert(is_same_v); -static_assert(is_same_v>); static_assert(is_same_v>); static_assert(equivalent); diff --git a/test/unit_test/static/point_origin_test.cpp b/test/unit_test/static/point_origin_test.cpp new file mode 100644 index 0000000000..8da58e4686 --- /dev/null +++ b/test/unit_test/static/point_origin_test.cpp @@ -0,0 +1,73 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include + +using namespace units; +namespace si = isq::si; + +namespace { + +struct width : kind {}; + +struct ones_viewpoint1 : point_origin { + template + using rebind = ones_viewpoint1; +}; + +struct ones_viewpoint2 : point_origin { + template + using rebind = ones_viewpoint1; +}; + +static_assert(PointOrigin>); +static_assert(!PointOrigin>); +static_assert(!PointOrigin); +static_assert(!PointOrigin>); + +static_assert(RebindablePointOriginFor, si::dim_length>); +static_assert(is_same_v, si::dim_length>, + dynamic_origin>); +static_assert(RebindablePointOriginFor, si::dim_time>); +static_assert(is_same_v, si::dim_time>, + dynamic_origin>); +static_assert(RebindablePointOriginFor); +static_assert(is_same_v, ones_viewpoint1>); +static_assert(RebindablePointOriginFor); +static_assert(is_same_v, ones_viewpoint1>); +static_assert(RebindablePointOriginFor); +static_assert(is_same_v, ones_viewpoint2>); +static_assert(!RebindablePointOriginFor); + +static_assert(equivalent, dynamic_origin>); +static_assert(equivalent, dynamic_origin>); +static_assert(!equivalent, clock_origin>); +static_assert(!equivalent, clock_origin>); +static_assert(!equivalent, dynamic_origin>); +static_assert(!equivalent); + +} // namespace diff --git a/test/unit_test/static/quantity_kind_test.cpp b/test/unit_test/static/quantity_kind_test.cpp index 5ffea7fe57..740d097343 100644 --- a/test/unit_test/static/quantity_kind_test.cpp +++ b/test/unit_test/static/quantity_kind_test.cpp @@ -82,16 +82,16 @@ static_assert(QuantityKind>); static_assert(QuantityKind>); static_assert(!QuantityKind); static_assert(!QuantityKind>); -static_assert(!QuantityKind>); +static_assert(!QuantityKind, metre>>); static_assert(QuantityKindOf, width_kind>); static_assert(!QuantityKindOf, height_kind>); static_assert(!QuantityKindOf, metre>); static_assert(!QuantityKindOf, width_kind>); static_assert(!QuantityKindOf, metre>); -static_assert(!QuantityKindOf, width_kind>); -static_assert(!QuantityKindOf, dim_length>); -static_assert(!QuantityKindOf, metre>); +static_assert(!QuantityKindOf, metre>, width_kind>); +static_assert(!QuantityKindOf, metre>, dim_length>); +static_assert(!QuantityKindOf, metre>, metre>); /////////////// @@ -105,7 +105,7 @@ template concept invalid_types = requires { requires !requires { typename quantity_kind; }; // unit of a different dimension requires !requires { typename quantity_kind>; }; // quantity used as Rep - requires !requires { typename quantity_kind>; }; // quantity point used as Rep + requires !requires { typename quantity_kind, metre>>; }; // quantity point used as Rep requires !requires { typename quantity_kind>; }; // quantity kind used as Rep requires !requires { typename quantity_kind; }; // reordered arguments requires !requires { typename quantity_kind; }; // reordered arguments @@ -454,7 +454,7 @@ concept invalid_compound_assignments = requires(quantity_kind requires invalid_compound_assignments_>; requires invalid_compound_assignments_>; requires invalid_compound_assignments_>; - requires invalid_compound_assignments_>; + requires invalid_compound_assignments_, metre, int>>; requires invalid_compound_assignments_; }; static_assert(invalid_compound_assignments); @@ -485,12 +485,12 @@ static_assert((width(0 * m) - width(1 static_assert(!std::is_invocable_v, width, double>); static_assert(!std::is_invocable_v, width, length>); -static_assert(!std::is_invocable_v, width, quantity_point>); +static_assert(!std::is_invocable_v, width, quantity_point, metre>>); static_assert(!std::is_invocable_v, width, height>); static_assert(!std::is_invocable_v, width, reference>); static_assert(!std::is_invocable_v, width, double>); static_assert(!std::is_invocable_v, width, length>); -static_assert(!std::is_invocable_v, width, quantity_point>); +static_assert(!std::is_invocable_v, width, quantity_point, metre>>); static_assert(!std::is_invocable_v, width, height>); static_assert(!std::is_invocable_v, width, reference>); @@ -650,17 +650,17 @@ static_assert(is_same_v< static_assert(!std::is_invocable_v, reference, width>); static_assert(!std::is_invocable_v, width, height>); -static_assert(!std::is_invocable_v, height, quantity_point>); -static_assert(!std::is_invocable_v, quantity_point, height>); +static_assert(!std::is_invocable_v, height, quantity_point, metre>>); +static_assert(!std::is_invocable_v, quantity_point, metre>, height>); static_assert(!std::is_invocable_v, reference, width>); static_assert(!std::is_invocable_v, width, height>); -static_assert(!std::is_invocable_v, height, quantity_point>); -static_assert(!std::is_invocable_v, quantity_point, height>); +static_assert(!std::is_invocable_v, height, quantity_point, metre>>); +static_assert(!std::is_invocable_v, quantity_point, metre>, height>); static_assert(!std::is_invocable_v, width, reference>); static_assert(!std::is_invocable_v, width, length>); -static_assert(!std::is_invocable_v, width, quantity_point>); +static_assert(!std::is_invocable_v, width, quantity_point, metre, int>>); static_assert(!std::is_invocable_v, width, double>); static_assert(!std::is_invocable_v, width, width>); @@ -801,8 +801,8 @@ concept invalid_cast = requires { requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; - requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; - requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; + requires !requires { quantity_kind_cast, metre, int>>(quantity_kind(1 * m)); }; + requires !requires { quantity_kind_cast, one, int>>(quantity_kind(1 * m)); }; }; static_assert(invalid_cast); diff --git a/test/unit_test/static/quantity_point_kind_test.cpp b/test/unit_test/static/quantity_point_kind_test.cpp index b92ba9b52b..7373856635 100644 --- a/test/unit_test/static/quantity_point_kind_test.cpp +++ b/test/unit_test/static/quantity_point_kind_test.cpp @@ -57,6 +57,18 @@ struct cgs_height_kind : kind {}; struct rate_of_climb_kind : derived_kind {}; struct altitude_kind : point_kind {}; +struct sea_level_origin : point_origin {}; +struct sea_level_altitude_kind : point_kind {}; + +template +struct screen_origin : point_origin { + template + using rebind = screen_origin; +}; +struct screen_si_width_kind : point_kind> {}; +struct screen_si_cgs_width_kind : + point_kind> {}; + struct apple : kind {}; struct orange : kind {}; struct nth_apple_kind : point_kind {}; @@ -65,6 +77,8 @@ struct nth_orange_kind : point_kind {}; struct time_kind : kind {}; struct time_point_kind : point_kind {}; +struct sys_time_point_kind : point_kind> {}; + template using width = quantity_kind; template using height = quantity_kind; template using abscissa = quantity_point_kind; @@ -76,6 +90,13 @@ template using cgs_height = quantity_kind< template using rate_of_climb = quantity_kind; template using altitude = quantity_point_kind; +template +using sea_level_altitude = quantity_point_kind; +template +using screen_si_width = quantity_point_kind; +template +using screen_si_cgs_width = quantity_point_kind; + template using apples = quantity_kind; template using oranges = quantity_kind; template using nth_apple = quantity_point_kind; @@ -89,7 +110,7 @@ static_assert(QuantityPointKind>); static_assert(QuantityPointKind>); static_assert(!QuantityPointKind); static_assert(!QuantityPointKind>); -static_assert(!QuantityPointKind>); +static_assert(!QuantityPointKind, metre>>); static_assert(!QuantityPointKind>); static_assert(QuantityPointKindOf, abscissa_kind>); @@ -100,9 +121,12 @@ static_assert(!QuantityPointKindOf, metre>); static_assert(!QuantityPointKindOf, abscissa_kind>); static_assert(!QuantityPointKindOf, width_kind>); static_assert(!QuantityPointKindOf, metre>); -static_assert(!QuantityPointKindOf, width_kind>); -static_assert(!QuantityPointKindOf, dim_length>); -static_assert(!QuantityPointKindOf, metre>); +static_assert(!QuantityPointKindOf, sea_level_altitude_kind>); +static_assert(!QuantityPointKindOf, sea_level_altitude_kind>); +static_assert(!QuantityPointKindOf, metre>, width_kind>); +static_assert(!QuantityPointKindOf, metre>, dim_length>); +static_assert(!QuantityPointKindOf, metre>, dynamic_origin>); +static_assert(!QuantityPointKindOf, metre>, metre>); /////////////// @@ -117,7 +141,7 @@ concept invalid_types = requires { requires !requires { typename quantity_point_kind; }; // width_kind is not a point kind requires !requires { typename quantity_point_kind; }; // unit of a different dimension requires !requires { typename quantity_point_kind>; }; // quantity used as Rep - requires !requires { typename quantity_point_kind>; }; // quantity point used as Rep + requires !requires { typename quantity_point_kind, metre>>; }; // quantity point used as Rep requires !requires { typename quantity_point_kind>; }; // quantity kind used as Rep requires !requires { typename quantity_point_kind>; }; // quantity point kind used as Rep requires !requires { typename quantity_point_kind; }; // reordered arguments @@ -160,6 +184,7 @@ static_assert(!std::is_aggregate_v>); static_assert(is_same_v::point_kind_type, abscissa_kind>); static_assert(is_same_v::kind_type, width_kind>); +static_assert(is_same_v::origin, dynamic_origin>); static_assert(is_same_v::quantity_kind_type, width>); static_assert(is_same_v::quantity_type, length>); static_assert(is_same_v::dimension, dim_length>); @@ -287,6 +312,14 @@ static_assert(!constructible_or_convertible_from>(quantity_ static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * (m * m)))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * s))); +static_assert(construct_from_only>(quantity_point, metre>(1 * m)) + .relative() + .common() == 1 * m); +static_assert(construct_from_only>(quantity_point, metre>(1 * m)) + .relative() + .common() == 1 * m); +static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * m))); + // clang-format off static_assert(construct_from_only>(quantity_point(1)).relative().common() == 1); static_assert(construct_from_only>(quantity_point(1)).relative().common() == 1); @@ -303,8 +336,12 @@ 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(1.0 * s))); -static_assert(construct_from_only>(sys_seconds{42s}).relative().common() == 42 * s); // clang-format on +static_assert( + construct_from_only>(sys_seconds{42s}).relative().common() == + 42 * s); +static_assert(!constructible_or_convertible_from>(sys_seconds{42s}), + "no implicit conversion to/from dynamic_origin"); ////////////////////////////////////// @@ -365,6 +402,15 @@ static_assert(!constructible_or_convertible_from>(nth_orange static_assert(!constructible_or_convertible_from>(abscissa(1 * m))); // clang-format on +static_assert(!constructible_or_convertible_from>( + quantity_point_kind{}), + "no implicit conversion to/from dynamic_origin"); +static_assert(!constructible_or_convertible_from>( + quantity_point_kind{}), + "no implicit conversion to/from dynamic_origin"); +static_assert(!constructible_or_convertible_from>(screen_si_cgs_width(1 * m)), + "base kinds are not the same (required by equivalent)"); + ////////////////////// // other conversions @@ -375,7 +421,7 @@ static_assert(!std::is_convertible_v, dimensionless, length>); static_assert(!std::is_convertible_v, width>); static_assert(!std::is_convertible_v, height>); -static_assert(!std::is_convertible_v, quantity_point>); +static_assert(!std::is_convertible_v, quantity_point, metre, int>>); //////////////////////// @@ -420,7 +466,7 @@ concept invalid_compound_assignments = requires(quantity_point_kind>; requires invalid_compound_assignments_>; requires invalid_compound_assignments_>; - requires invalid_compound_assignments_>; + requires invalid_compound_assignments_, metre, int>>; requires invalid_compound_assignments_; }; static_assert(invalid_compound_assignments); @@ -447,13 +493,13 @@ static_assert(same(width(2 * km) + abscissa(3e3 * static_assert(same(width(2e3 * m) + abscissa(3 * km), abscissa(5e3 * m))); static_assert(!std::is_invocable_v, abscissa, double>); static_assert(!std::is_invocable_v, abscissa, length>); -static_assert(!std::is_invocable_v, abscissa, quantity_point>); +static_assert(!std::is_invocable_v, abscissa, quantity_point, metre>>); static_assert(!std::is_invocable_v, abscissa, height>); static_assert(!std::is_invocable_v, abscissa, abscissa>); static_assert(!std::is_invocable_v, abscissa, abscissa>); static_assert(!std::is_invocable_v, abscissa, abscissa>); static_assert(!std::is_invocable_v, height, abscissa>); -static_assert(!std::is_invocable_v, quantity_point, abscissa>); +static_assert(!std::is_invocable_v, quantity_point, metre>, abscissa>); static_assert(!std::is_invocable_v, length, abscissa>); static_assert(!std::is_invocable_v, double, abscissa>); @@ -469,14 +515,15 @@ static_assert(same(abscissa(2 * km) - abscissa(3e static_assert(same(abscissa(2e3 * m) - abscissa(3 * km), width(-1e3 * m))); static_assert(!std::is_invocable_v, abscissa, double>); static_assert(!std::is_invocable_v, abscissa, length>); -static_assert(!std::is_invocable_v, abscissa, quantity_point>); +static_assert(!std::is_invocable_v, abscissa, quantity_point, metre>>); static_assert(!std::is_invocable_v, abscissa, height>); static_assert(!std::is_invocable_v, abscissa, ordinate>); static_assert(!std::is_invocable_v, ordinate, abscissa>); static_assert(!std::is_invocable_v, height, abscissa>); -static_assert(!std::is_invocable_v, quantity_point, abscissa>); +static_assert(!std::is_invocable_v, quantity_point, metre>, abscissa>); static_assert(!std::is_invocable_v, length, abscissa>); static_assert(!std::is_invocable_v, double, abscissa>); +static_assert(!std::is_invocable_v, screen_si_width, screen_si_cgs_width>); // clang-format on @@ -496,8 +543,8 @@ static_assert(std::equality_comparable_with, abscissa, abscissa>); static_assert(std::equality_comparable_with, abscissa>); // clang-format on -template -concept invalid_equality = requires(quantity_point_kind x) { +template +concept invalid_equality = requires(quantity_point_kind x, Int i) { requires !requires { x == 1; }; requires !requires { x != 1.0; }; requires !requires { x == 1 * m; }; @@ -516,8 +563,9 @@ concept invalid_equality = requires(quantity_point_kind x) requires !requires { x == quantity_point(dimensionless(1.0)); }; requires !requires { x != quantity_point_kind(cgs_width(1 * m)); }; requires !requires { x == ordinate(1 * m); }; + requires !requires { screen_si_width{} != screen_si_cgs_width{}; }; }; -static_assert(invalid_equality); +static_assert(invalid_equality); // clang-format off static_assert(abscissa(1 * m) < abscissa(2 * m)); @@ -531,8 +579,8 @@ static_assert(std::three_way_comparable_with, abscissa< static_assert(std::three_way_comparable_with, abscissa>); static_assert(std::three_way_comparable_with, abscissa>); // clang-format on -template -concept invalid_relational = requires(quantity_point_kind x) { +template +concept invalid_relational = requires(quantity_point_kind x, Int i) { requires !requires { x < 1; }; requires !requires { x <= 1.0; }; requires !requires { x >= 1 * m; }; @@ -551,8 +599,9 @@ concept invalid_relational = requires(quantity_point_kind requires !requires { x < quantity_point(dimensionless(1.0)); }; requires !requires { x <= quantity_point_kind(cgs_width(1 * m)); }; requires !requires { x >= ordinate(1 * m); }; + requires !requires { screen_si_width{} > screen_si_cgs_width{}; }; }; -static_assert(invalid_relational); +static_assert(invalid_relational); ///////////////////////////// @@ -600,6 +649,7 @@ static_assert(same(quantity_point_kind_cast>(abscissa>(abscissa(1 * m)), abscissa(100 * cm))); static_assert(same(quantity_point_kind_cast>(abscissa(0.01 * m)), abscissa(1 * cm))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * cgs_cm)), abscissa(1 * cgs_cm))); +static_assert(same(quantity_point_kind_cast>(screen_si_width(1 * m)), screen_si_cgs_width(1 * m))); // clang-format on template concept invalid_cast = requires(Int i) { @@ -617,8 +667,9 @@ concept invalid_cast = requires(Int i) { requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; - requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; - requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; + requires !requires { quantity_point_kind_cast, metre, Int>>(abscissa(i * m)); }; + requires !requires { quantity_point_kind_cast, one, Int>>(abscissa(i * m)); }; + requires !requires { quantity_point_kind_cast, metre, Int>>(screen_si_width(i * m)); }; }; static_assert(invalid_cast); diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index ff7771c936..d8e810d95e 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -41,72 +41,89 @@ using namespace references; using namespace std::chrono_literals; using sys_seconds = std::chrono::time_point; +struct sea_level_origin : point_origin {}; + // class invariants template concept invalid_types = requires { - requires !requires { typename quantity_point; }; // unit of a different dimension - requires !requires { typename quantity_point>; }; // quantity used as Rep - // quantity point used as Rep - requires !requires { typename quantity_point>; }; - requires !requires { typename quantity; }; // reordered arguments + // unit of a different dimension: + requires !requires { typename quantity_point, second, int>; }; + // quantity used as Rep: + requires !requires { typename quantity_point, metre, quantity>; }; + // quantity point used as Rep: + requires !requires { typename quantity_point, metre, + quantity_point, metre, int>>; }; + // reordered arguments: + requires !requires { typename quantity_point, double>; }; + // dimension used as origin: + requires !requires { typename quantity_point; }; }; static_assert(invalid_types); // member types -static_assert(is_same_v::rep, int>); -static_assert(is_same_v::rep, double>); -static_assert(is_same_v::unit, metre>); -static_assert(is_same_v::unit, kilometre>); -static_assert(is_same_v::dimension, dim_length>); -static_assert(is_same_v::quantity_type, quantity>); +static_assert(is_same_v, metre, int>::rep, int>); +static_assert(is_same_v, metre, double>::rep, double>); +static_assert(is_same_v, metre, int>::unit, metre>); +static_assert(is_same_v, kilometre, int>::unit, kilometre>); +static_assert(is_same_v, metre, int>::dimension, dim_length>); +static_assert(is_same_v, metre, int>::origin, dynamic_origin>); +static_assert(is_same_v, metre, int>::quantity_type, quantity>); // constructors static_assert(quantity_point(1).relative() == quantity(1)); -static_assert(!std::is_convertible_v>); +static_assert(!std::is_convertible_v, one, int>>); 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(!std::is_convertible_v, second, std::chrono::seconds::rep>>); +static_assert(!std::is_convertible_v, second, std::chrono::seconds::rep>>); +static_assert(!std::is_convertible_v, second, sys_seconds::rep>>); +static_assert(!std::is_convertible_v, second, sys_seconds::rep>>); -static_assert(quantity_point().relative() == 0_q_m); -constexpr quantity_point km{1000_q_m}; +static_assert(quantity_point, metre, int>().relative() == 0_q_m); +constexpr quantity_point, metre, int> km{1000_q_m}; static_assert(km.relative() == 1000_q_m); -static_assert(quantity_point(km).relative() == km.relative()); - -static_assert(quantity_point(1_q_m).relative() == 1_q_m); -static_assert(!std::is_constructible_v, double>); // truncating conversion -static_assert(quantity_point(1.0_q_m).relative() == 1.0_q_m); -static_assert(quantity_point(1_q_m).relative() == 1_q_m); -static_assert(quantity_point(3.14_q_m).relative() == 3.14_q_m); - -static_assert(quantity_point(km).relative() == 1000_q_m); -static_assert(!std::is_constructible_v, - quantity_point>); // truncating conversion -static_assert(quantity_point(quantity_point(1000.0_q_m)).relative() == 1000.0_q_m); -static_assert(quantity_point(km).relative() == 1000.0_q_m); -static_assert(quantity_point(quantity_point(1_q_km)).relative() == 1000_q_m); -static_assert(!std::is_constructible_v, - quantity_point>); // different dimensions -static_assert(!std::is_constructible_v, - quantity_point>); // truncating conversion +static_assert(quantity_point, metre, int>(km).relative() == km.relative()); + +static_assert(quantity_point, metre, int>(1_q_m).relative() == 1_q_m); +static_assert(!std::is_constructible_v, metre, int>, double>); // truncating conversion +static_assert(quantity_point, metre, double>(1.0_q_m).relative() == 1.0_q_m); +static_assert(quantity_point, metre, double>(1_q_m).relative() == 1_q_m); +static_assert(quantity_point, metre, long double>(3.14_q_m).relative() == 3.14_q_m); + +static_assert(quantity_point, metre, int>(km).relative() == 1000_q_m); +static_assert(!std::is_constructible_v, metre, int>, + quantity_point, metre, double>>); // truncating conversion +static_assert(quantity_point, metre, double>(quantity_point(1000.0_q_m)).relative() == 1000.0_q_m); +static_assert(quantity_point, metre, double>(km).relative() == 1000.0_q_m); +static_assert(quantity_point, metre, int>(quantity_point(1_q_km)).relative() == 1000_q_m); +static_assert(!std::is_constructible_v, metre, int>, + quantity_point, second, int>>); // different dimensions +static_assert(!std::is_constructible_v, kilometre, int>, + quantity_point, metre, int>>); // truncating conversion + +static_assert(!std::is_constructible_v, + quantity_point, metre, int>>, + "non-equivalent origins"); +static_assert(!std::is_constructible_v, second, int>, sys_seconds>, + "non-equivalent origins, no implicit conversion from `clock_origin`"); // assignment operator -static_assert([]() { quantity_point l1(1_q_m), l2{}; return l2 = l1; }().relative() == 1_q_m); +static_assert([]() { quantity_point, metre, int> l1(1_q_m), l2{}; return l2 = l1; }().relative() == 1_q_m); // static member functions -static_assert(quantity_point::min().relative().number() == std::numeric_limits::lowest()); -static_assert(quantity_point::max().relative().number() == std::numeric_limits::max()); -static_assert(quantity_point::min().relative().number() == +static_assert(quantity_point, metre, int>::min().relative().number() == std::numeric_limits::lowest()); +static_assert(quantity_point, metre, int>::max().relative().number() == std::numeric_limits::max()); +static_assert(quantity_point, metre, double>::min().relative().number() == std::numeric_limits::lowest()); -static_assert(quantity_point::max().relative().number() == +static_assert(quantity_point, metre, double>::max().relative().number() == std::numeric_limits::max()); // unary member operators @@ -114,19 +131,19 @@ static_assert(quantity_point::max().relative().number static_assert([](auto v) { auto vv = v++; return std::pair(v, vv); -}(km) == std::pair(quantity_point(1001_q_m), quantity_point(1000_q_m))); +}(km) == std::pair(quantity_point, metre, int>(1001_q_m), quantity_point, metre, int>(1000_q_m))); static_assert([](auto v) { auto vv = ++v; return std::pair(v, vv); -}(km) == std::pair(quantity_point(1001_q_m), quantity_point(1001_q_m))); +}(km) == std::pair(quantity_point, metre, int>(1001_q_m), quantity_point, metre, int>(1001_q_m))); static_assert([](auto v) { auto vv = v--; return std::pair(v, vv); -}(km) == std::pair(quantity_point(999_q_m), quantity_point(1000_q_m))); +}(km) == std::pair(quantity_point, metre, int>(999_q_m), quantity_point, metre, int>(1000_q_m))); static_assert([](auto v) { auto vv = --v; return std::pair(v, vv); -}(km) == std::pair(quantity_point(999_q_m), quantity_point(999_q_m))); +}(km) == std::pair(quantity_point, metre, int>(999_q_m), quantity_point, metre, int>(999_q_m))); // compound assignment @@ -135,23 +152,23 @@ static_assert((quantity_point(2_q_m) -= 1_q_m).relative().number() == 1); // non-member arithmetic operators -static_assert(compare() + length()), - quantity_point>); -static_assert(compare() + quantity_point()), - quantity_point>); -static_assert(compare() + length()), - quantity_point>); -static_assert(compare() + quantity_point()), - quantity_point>); -static_assert(compare() - length()), - quantity_point>); -static_assert(compare() - length()), - quantity_point>); +static_assert(compare, metre, int>() + length()), + quantity_point, metre, double>>); +static_assert(compare() + quantity_point, metre, double>()), + quantity_point, metre, double>>); +static_assert(compare, kilometre, int>() + length()), + quantity_point, metre, double>>); +static_assert(compare() + quantity_point, metre, double>()), + quantity_point, metre, double>>); +static_assert(compare, metre, double>() - length()), + quantity_point, metre, double>>); +static_assert(compare, kilometre, double>() - length()), + quantity_point, metre, double>>); static_assert( - compare() - quantity_point()), + compare, metre, double>() - quantity_point, metre, int>()), length>); static_assert( - compare() - quantity_point()), + compare, kilometre, double>() - quantity_point, metre, int>()), length>); static_assert((1_q_m + km).relative().number() == 1001); @@ -159,6 +176,13 @@ static_assert((quantity_point(1_q_m) + 1_q_km).relative().number() == 1001); static_assert((km - 1_q_m).relative().number() == 999); static_assert((quantity_point(1_q_km) - quantity_point(1_q_m)).number() == 999); +template +concept invalid_subtraction = requires(quantity_point, metre, Int> lhs, + quantity_point rhs) { + requires !requires { rhs - lhs; }; +}; +static_assert(invalid_subtraction); + // comparators static_assert(quantity_point(2_q_m) + 1_q_m == quantity_point(3_q_m)); @@ -190,6 +214,14 @@ static_assert(quantity_point(999_q_m) < quantity_point(1_q_km)); static_assert(quantity_point(1000_q_m) >= quantity_point(1_q_km)); static_assert(quantity_point(1000_q_m) <= quantity_point(1_q_km)); +template +concept invalid_comparisons = requires(quantity_point, metre, Int> lhs, + quantity_point rhs) { + requires !requires { lhs == rhs; }; + requires !requires { lhs < rhs; }; +}; +static_assert(invalid_comparisons); + // alias units static_assert(quantity_point(2_q_l) + 2_q_ml == quantity_point(2002_q_ml)); @@ -199,19 +231,19 @@ static_assert(2_q_dm3 + quantity_point(2_q_cm3) == quantity_point(2002_q_ml)); // is_quantity_point -static_assert(QuantityPoint>); +static_assert(QuantityPoint, millimetre, int>>); // common_quantity_point static_assert(compare< - common_quantity_point, quantity_point>, - quantity_point>); -static_assert(compare, - quantity_point>, - quantity_point>); -static_assert(compare, - quantity_point>, - quantity_point>); + common_quantity_point, metre, int>, quantity_point, kilometre, int>>, + quantity_point, metre, int>>); +static_assert(compare, kilometre, long long>, + quantity_point, metre, int>>, + quantity_point, metre, long long>>); +static_assert(compare, kilometre, long long>, + quantity_point, millimetre, double>>, + quantity_point, millimetre, double>>); // common_type @@ -224,11 +256,11 @@ static_assert(std::equality_comparable_with>(quantity_point(2_q_km)).relative().number() == +static_assert(quantity_point_cast, metre, int>>(quantity_point(2_q_km)).relative().number() == 2000); static_assert( - quantity_point_cast>(quantity_point(2000_q_m)).relative().number() == 2); -static_assert(quantity_point_cast>(quantity_point(1.23_q_m)).relative().number() == + quantity_point_cast, kilometre, int>>(quantity_point(2000_q_m)).relative().number() == 2); +static_assert(quantity_point_cast, metre, int>>(quantity_point(1.23_q_m)).relative().number() == 1); static_assert(quantity_point_cast>(quantity_point(2_q_km)).relative().number() == 2000); static_assert(quantity_point_cast>(quantity_point(2000_q_m)).relative().number() == 2); @@ -238,6 +270,13 @@ static_assert(quantity_point_cast(quantity_point(2000_q_m)).relative( static_assert(quantity_point_cast(quantity_point(1.23_q_m)).relative().number() == 1); static_assert(quantity_point_cast(quantity_point(2000.0_q_m / 3600.0_q_s)).relative().number() == 2); +template +concept invalid_cast = requires(Int i) { + requires !requires { quantity_point_cast, second, Int>>(quantity_point(i * m)); }; + requires !requires { quantity_point_cast, metre, Int>>(quantity_point(i * m)); }; +}; +static_assert(invalid_cast); + // time static_assert(quantity_point{1_q_h} == quantity_point{3600_q_s}); @@ -263,6 +302,6 @@ concept dimensional_analysis = requires(T t) pow<2>(t); }; -static_assert(!dimensional_analysis>); +static_assert(!dimensional_analysis, metre, int>>); } // namespace