From f2be389b578841699c2c71ca0b9045ddcc0d6265 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 18 Jan 2024 16:47:16 +0100 Subject: [PATCH 01/18] feat: `.view()` member funciton added to `fixed_string` --- src/core/include/mp-units/bits/external/fixed_string.h | 5 +++++ test/static/fixed_string_test.cpp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/core/include/mp-units/bits/external/fixed_string.h b/src/core/include/mp-units/bits/external/fixed_string.h index 025d1c60..35e39738 100644 --- a/src/core/include/mp-units/bits/external/fixed_string.h +++ b/src/core/include/mp-units/bits/external/fixed_string.h @@ -85,6 +85,11 @@ struct basic_fixed_string { [[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); } [[nodiscard]] constexpr const_iterator cend() const noexcept { return data() + size(); } + [[nodiscard]] constexpr std::basic_string_view view() const noexcept + { + return std::basic_string_view(*this); + } + template [[nodiscard]] constexpr friend basic_fixed_string operator+( const basic_fixed_string& lhs, const basic_fixed_string& rhs) noexcept diff --git a/test/static/fixed_string_test.cpp b/test/static/fixed_string_test.cpp index c4a56aed..a40677d1 100644 --- a/test/static/fixed_string_test.cpp +++ b/test/static/fixed_string_test.cpp @@ -54,4 +54,8 @@ static_assert(basic_fixed_string('d') + txt2 == basic_fixed_string("dabc")); static_assert(txt2 + basic_fixed_string("def") == basic_fixed_string("abcdef")); static_assert(basic_fixed_string("def") + txt2 == basic_fixed_string("defabc")); +#ifndef MP_UNITS_COMP_GCC +static_assert(basic_fixed_string("abcd").view().find('c') == 2); +#endif + } // namespace From 12aebbb08ba47784196c646835fe2a0694e5e840 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 18 Jan 2024 16:49:11 +0100 Subject: [PATCH 02/18] refactor: `fixed_string::op[]` now returns by value --- src/core/include/mp-units/bits/external/fixed_string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/mp-units/bits/external/fixed_string.h b/src/core/include/mp-units/bits/external/fixed_string.h index 35e39738..33aed7cf 100644 --- a/src/core/include/mp-units/bits/external/fixed_string.h +++ b/src/core/include/mp-units/bits/external/fixed_string.h @@ -78,7 +78,7 @@ struct basic_fixed_string { [[nodiscard]] constexpr size_type size() const noexcept { return N; } [[nodiscard]] constexpr const_pointer data() const noexcept { return data_; } [[nodiscard]] constexpr const CharT* c_str() const noexcept { return data(); } - [[nodiscard]] constexpr const_reference operator[](size_type index) const noexcept { return data()[index]; } + [[nodiscard]] constexpr value_type operator[](size_type index) const noexcept { return data()[index]; } [[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); } [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return data(); } From 7296238223699497d90bd192029576c8126abbbd Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 18 Jan 2024 16:50:10 +0100 Subject: [PATCH 03/18] refactor: :boom: `fixed_string` constructor taking a single character made explicit --- src/core/include/mp-units/bits/external/fixed_string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/mp-units/bits/external/fixed_string.h b/src/core/include/mp-units/bits/external/fixed_string.h index 33aed7cf..0e97c20c 100644 --- a/src/core/include/mp-units/bits/external/fixed_string.h +++ b/src/core/include/mp-units/bits/external/fixed_string.h @@ -56,7 +56,7 @@ struct basic_fixed_string { using size_type = std::size_t; using difference_type = std::ptrdiff_t; - constexpr explicit(false) basic_fixed_string(CharT ch) noexcept + constexpr explicit basic_fixed_string(CharT ch) noexcept requires(N == 1) { data_[0] = ch; From 901e718deb4cf0f4a2580aa86b8cb28b4160a083 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 18 Jan 2024 18:07:07 +0100 Subject: [PATCH 04/18] feat: text output support added to `basic_fixed_string` --- .../mp-units/bits/external/fixed_string.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/core/include/mp-units/bits/external/fixed_string.h b/src/core/include/mp-units/bits/external/fixed_string.h index 0e97c20c..d595f907 100644 --- a/src/core/include/mp-units/bits/external/fixed_string.h +++ b/src/core/include/mp-units/bits/external/fixed_string.h @@ -29,6 +29,7 @@ // IWYU pragma: begin_exports #include #include +#include // IWYU pragma: end_exports #include @@ -121,6 +122,13 @@ struct basic_fixed_string { // TODO std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); return detail::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, + const basic_fixed_string& str) + { + return os << str.c_str(); + } }; template @@ -136,3 +144,12 @@ template using fixed_string = basic_fixed_string; } // namespace mp_units + +template +struct MP_UNITS_STD_FMT::formatter> : formatter> { + template + auto format(const mp_units::basic_fixed_string& str, FormatContext& ctx) + { + return formatter>::format(str.view(), ctx); + } +}; From 5183a5c830265269891ba806f9f39f0434355783 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 18 Jan 2024 18:41:29 +0100 Subject: [PATCH 05/18] fix: creation of `std::string_view` in `basic_fixed_string::view()` now is compatible with C++20 --- src/core/include/mp-units/bits/external/fixed_string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/mp-units/bits/external/fixed_string.h b/src/core/include/mp-units/bits/external/fixed_string.h index d595f907..eec60ed4 100644 --- a/src/core/include/mp-units/bits/external/fixed_string.h +++ b/src/core/include/mp-units/bits/external/fixed_string.h @@ -88,7 +88,7 @@ struct basic_fixed_string { [[nodiscard]] constexpr std::basic_string_view view() const noexcept { - return std::basic_string_view(*this); + return std::basic_string_view(cbegin(), cend()); } template From 40dcc9de660556b25c9d38551ff39d4065617da4 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 18 Jan 2024 18:59:10 +0100 Subject: [PATCH 06/18] feat: `basic_fixed_string` contructor now can take a list of arguments --- .../mp-units/bits/external/fixed_string.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/include/mp-units/bits/external/fixed_string.h b/src/core/include/mp-units/bits/external/fixed_string.h index eec60ed4..f1a0fa3a 100644 --- a/src/core/include/mp-units/bits/external/fixed_string.h +++ b/src/core/include/mp-units/bits/external/fixed_string.h @@ -57,18 +57,18 @@ struct basic_fixed_string { using size_type = std::size_t; using difference_type = std::ptrdiff_t; - constexpr explicit basic_fixed_string(CharT ch) noexcept - requires(N == 1) - { - data_[0] = ch; - } - constexpr explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept { if constexpr (N != 0) for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i]; } + template... Rest> + requires(1 + sizeof...(Rest) == N) + constexpr explicit basic_fixed_string(CharT first, Rest... rest) noexcept : data_{first, rest..., CharT('\0')} + { + } + constexpr basic_fixed_string(const CharT* ptr, std::integral_constant) noexcept { if constexpr (N != 0) @@ -131,12 +131,12 @@ struct basic_fixed_string { } }; -template -basic_fixed_string(CharT) -> basic_fixed_string; - template basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string; +template... Rest> +basic_fixed_string(CharT, Rest...) -> basic_fixed_string; + template basic_fixed_string(const CharT* ptr, std::integral_constant) -> basic_fixed_string; From 81871bfd4c8f9b72f9605108209362b23a842851 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 18 Jan 2024 19:00:06 +0100 Subject: [PATCH 07/18] fix: `fixed_string` should not expose `` --- src/core/include/mp-units/bits/external/fixed_string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/mp-units/bits/external/fixed_string.h b/src/core/include/mp-units/bits/external/fixed_string.h index f1a0fa3a..1ad0a5b4 100644 --- a/src/core/include/mp-units/bits/external/fixed_string.h +++ b/src/core/include/mp-units/bits/external/fixed_string.h @@ -29,7 +29,7 @@ // IWYU pragma: begin_exports #include #include -#include +#include // IWYU pragma: end_exports #include From b4f07f6e7c50f60b479e75ae8cecc643ef1dd7a6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 19 Jan 2024 10:35:32 +0100 Subject: [PATCH 08/18] refactor: :boom: `ReferenceOf` does not take a dimension anymore It seems unneeded and removing this check greatly simplifies common compilation errors. --- docs/users_guide/framework_basics/concepts.md | 18 ++++++--------- .../include/mp-units/bits/quantity_concepts.h | 10 ++++----- .../mp-units/bits/quantity_point_concepts.h | 8 +++---- .../mp-units/bits/reference_concepts.h | 12 +++++----- test/static/concepts_test.cpp | 22 +++++-------------- 5 files changed, 27 insertions(+), 43 deletions(-) diff --git a/docs/users_guide/framework_basics/concepts.md b/docs/users_guide/framework_basics/concepts.md index ec304afa..5faab730 100644 --- a/docs/users_guide/framework_basics/concepts.md +++ b/docs/users_guide/framework_basics/concepts.md @@ -165,12 +165,8 @@ A `Reference` can either be: ### `ReferenceOf` { #ReferenceOf } -`ReferenceOf` concept is satisfied by references `T` that match the following value `V`: - -| `V` | Condition | -|----------------|-----------------------------------------------------------------------------------------------| -| `Dimension` | The dimension of a quantity specification satisfies [`DimensionOf`](#DimensionOf) concept. | -| `QuantitySpec` | The quantity specification satisfies [`QuantitySpecOf`](#QuantitySpecOf) concept. | +`ReferenceOf` concept is satisfied by references `T` which have a quantity specification that satisfies +[`QuantitySpecOf`](#QuantitySpecOf) concept. | ## `Representation` { #Representation } @@ -212,7 +208,7 @@ satisfied by all types being or deriving from an instantiation of a `quantity` c ### `QuantityOf` { #QuantityOf } -`QuantityOf` concept is satisfied by all the quantities for which a [`ReferenceOf`](#ReferenceOf) +`QuantityOf` concept is satisfied by all the quantities for which a [`QuantitySpecOf`](#QuantitySpecOf) is `true`. @@ -259,10 +255,10 @@ class template. `QuantityPointOf` concept is satisfied by all the quantity points `T` that match the following value `V`: -| `V` | Condition | -|---------------|----------------------------------------------------------------------------------| -| `Reference` | The quantity point reference satisfies [`ReferenceOf`](#ReferenceOf) concept. | -| `PointOrigin` | The _point_ and `V` have the same absolute point origin. | +| `V` | Condition | +|----------------|-----------------------------------------------------------------------------------------------------| +| `QuantitySpec` | The quantity point quantity specification satisfies [`QuantitySpecOf`](#QuantitySpecOf) concept. | +| `PointOrigin` | The _point_ and `V` have the same absolute point origin. | ## `QuantityLike` { #QuantityLike } diff --git a/src/core/include/mp-units/bits/quantity_concepts.h b/src/core/include/mp-units/bits/quantity_concepts.h index 83d9c209..bcebab84 100644 --- a/src/core/include/mp-units/bits/quantity_concepts.h +++ b/src/core/include/mp-units/bits/quantity_concepts.h @@ -52,13 +52,13 @@ template concept Quantity = detail::is_derived_from_specialization_of_quantity; /** - * @brief A concept matching all quantities with provided dimension or quantity spec + * @brief A concept matching all quantities with provided quantity spec * - * Satisfied by all quantities with a dimension/quantity_spec being the instantiation derived from - * the provided dimension/quantity_spec type. + * Satisfied by all quantities with a quantity_spec being the instantiation derived from + * the provided quantity_spec type. */ -template -concept QuantityOf = Quantity && ReferenceOf, V>; +template +concept QuantityOf = Quantity && QuantitySpecOf, QS>; /** * @brief A concept matching all external quantities like types diff --git a/src/core/include/mp-units/bits/quantity_point_concepts.h b/src/core/include/mp-units/bits/quantity_point_concepts.h index 6488a096..765717a3 100644 --- a/src/core/include/mp-units/bits/quantity_point_concepts.h +++ b/src/core/include/mp-units/bits/quantity_point_concepts.h @@ -145,15 +145,15 @@ concept SameAbsolutePointOriginAs = /** - * @brief A concept matching all quantity points with provided dimension or quantity spec + * @brief A concept matching all quantity points with provided quantity spec * - * Satisfied by all quantity points with a dimension/quantity_spec being the instantiation derived from - * the provided dimension/quantity_spec type, or quantity points having the origin with the same + * Satisfied by all quantity points with a quantity_spec being the instantiation derived from + * the provided quantity_spec type, or quantity points having the origin with the same * `absolute_point_origin`. */ template concept QuantityPointOf = - QuantityPoint && (ReferenceOf, V> || + QuantityPoint && (QuantitySpecOf, V> || detail::SameAbsolutePointOriginAs, V>); /** diff --git a/src/core/include/mp-units/bits/reference_concepts.h b/src/core/include/mp-units/bits/reference_concepts.h index 849d0427..57299f31 100644 --- a/src/core/include/mp-units/bits/reference_concepts.h +++ b/src/core/include/mp-units/bits/reference_concepts.h @@ -66,14 +66,12 @@ template } /** - * @brief A concept matching all references with provided dimension or quantity spec + * @brief A concept matching all references with provided quantity spec * - * Satisfied by all references with a dimension/quantity_spec being the instantiation derived from - * the provided dimension/quantity_spec type. + * Satisfied by all references with a quantity_spec being the instantiation derived from + * the provided quantity_spec type. */ -template -concept ReferenceOf = - Reference && (DimensionOf, V> || - QuantitySpecOf, V>); +template +concept ReferenceOf = Reference && QuantitySpecOf, QS>; } // namespace mp_units diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index 3bfd265d..4d443a8d 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -82,6 +82,9 @@ static_assert(!Dimension>); static_assert(!Dimension); static_assert(!Dimension); +// DimensionOf +// TODO add tests + // QuantitySpec struct speed : decltype(isq::length / isq::time) {}; // this is not recommended @@ -132,6 +135,9 @@ static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec); +// QuantitySpecOf +// TODO add tests + // Unit struct metre_per_second : decltype(si::metre / si::second) {}; @@ -265,24 +271,16 @@ static_assert(ReferenceOf, static_assert(!ReferenceOf, isq::radius>); static_assert(ReferenceOf, isq::length>); static_assert(ReferenceOf, isq::radius>); -static_assert(ReferenceOf); static_assert(!ReferenceOf); -static_assert(ReferenceOf, isq::dim_length>); -static_assert(ReferenceOf, isq::dim_length>); static_assert(ReferenceOf); -static_assert(ReferenceOf); static_assert(ReferenceOf, dimensionless>); -static_assert(ReferenceOf, dimension_one>); static_assert(ReferenceOf, isq::rotation>); static_assert(ReferenceOf, dimensionless>); -static_assert(ReferenceOf, dimension_one>); static_assert(ReferenceOf); static_assert(!ReferenceOf); -static_assert(ReferenceOf); static_assert(ReferenceOf, isq::angular_measure>); static_assert(!ReferenceOf, dimensionless>); -static_assert(ReferenceOf, dimension_one>); static_assert(ReferenceOf); static_assert(ReferenceOf); static_assert(!ReferenceOf, isq::rotation>); @@ -323,24 +321,16 @@ static_assert(QuantityOf, isq::length>); static_assert(!QuantityOf, isq::radius>); static_assert(QuantityOf, isq::length>); static_assert(QuantityOf, isq::radius>); -static_assert(QuantityOf, isq::dim_length>); static_assert(!QuantityOf, isq::dim_length>); -static_assert(QuantityOf, isq::dim_length>); -static_assert(QuantityOf, isq::dim_length>); static_assert(QuantityOf, dimensionless>); -static_assert(QuantityOf, dimension_one>); static_assert(QuantityOf, dimensionless>); -static_assert(QuantityOf, dimension_one>); static_assert(QuantityOf, isq::rotation>); static_assert(QuantityOf, dimensionless>); -static_assert(QuantityOf, dimension_one>); static_assert(QuantityOf, isq::angular_measure>); static_assert(!QuantityOf, dimensionless>); -static_assert(QuantityOf, dimension_one>); static_assert(QuantityOf, isq::angular_measure>); static_assert(!QuantityOf, dimensionless>); -static_assert(QuantityOf, dimension_one>); static_assert(QuantityOf, isq::rotation>); static_assert(QuantityOf, isq::angular_measure>); static_assert(!QuantityOf, isq::rotation>); From d036af564749b28f0f4abdb00f97863ead68f25e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 19 Jan 2024 11:09:29 +0100 Subject: [PATCH 09/18] fix: compilation errors after the last commit fixed --- src/core/include/mp-units/quantity.h | 4 ++-- test/static/quantity_test.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/include/mp-units/quantity.h b/src/core/include/mp-units/quantity.h index 28dddb91..ace7c31c 100644 --- a/src/core/include/mp-units/quantity.h +++ b/src/core/include/mp-units/quantity.h @@ -362,7 +362,7 @@ public: return std::forward(lhs); } - template Q2> + template Q2> requires std::derived_from, quantity> && (Q2::unit == ::mp_units::one) && requires(rep a, const typename Q2::rep b) { { @@ -389,7 +389,7 @@ public: return std::forward(lhs); } - template Q2> + template Q2> requires std::derived_from, quantity> && (Q2::unit == ::mp_units::one) && requires(rep a, const typename Q2::rep b) { { diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index 891cf21d..7992f493 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -958,7 +958,6 @@ static_assert(QuantityOf, isq::position_vector / isq::duration>) static_assert(QuantityOf[m / s]>, isq::position_vector / isq::duration>); static_assert(QuantityOf, isq::position_vector / isq::duration>); -static_assert(QuantityOf); static_assert(QuantityOf); // kind of static_assert(QuantityOf[m]), isq::height>); // kind of static_assert(!QuantityOf); // different kinds From fc7e1358cbbe66f249f471e423832e2d62193d9a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 19 Jan 2024 16:03:21 +0100 Subject: [PATCH 10/18] feat: `underlying_type` type trait added --- .../mp-units/bits/external/type_traits.h | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/core/include/mp-units/bits/external/type_traits.h b/src/core/include/mp-units/bits/external/type_traits.h index 65223343..f98db6c9 100644 --- a/src/core/include/mp-units/bits/external/type_traits.h +++ b/src/core/include/mp-units/bits/external/type_traits.h @@ -83,6 +83,36 @@ template typename Type> // inline constexpr bool // TODO: Replace with concept when it works with MSVC concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of(t); }; + +namespace detail { + +template +struct get_value_type { + using type = MP_UNITS_TYPENAME T::value_type; +}; + +template +struct get_element_type { + using type = std::remove_reference_t; +}; + +} // namespace detail + +template +struct underlying_type { + using type = T; +}; + +template + requires requires { typename T::value_type; } || requires { typename T::element_type; } +struct underlying_type { + using type = MP_UNITS_TYPENAME + conditional, detail::get_element_type>::type; +}; + +template +using underlying_type_t = MP_UNITS_TYPENAME underlying_type::type; + template concept one_of = (false || ... || std::same_as); From 840b0bd54e05bec9423fbdb539cac2eda13284c4 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 19 Jan 2024 16:04:55 +0100 Subject: [PATCH 11/18] fix: performace regression in `sudo_cast` fixed --- src/core/include/mp-units/bits/sudo_cast.h | 45 +++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index 2ce05e7f..65efb882 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -30,17 +30,12 @@ namespace mp_units::detail { -// determines the best available representation type -template -[[nodiscard]] consteval auto common_rep_type(From, To) -{ - if constexpr (requires { typename std::common_type_t; }) - // returns a common type of two representation types if available - // e.g. `double` and `int` will end up with `double` precision - return std::common_type_t{}; - else - return typename From::rep{}; -} +template +struct get_common_type : std::common_type {}; + +template +using maybe_common_type = MP_UNITS_TYPENAME std::conditional_t; }, + get_common_type, std::type_identity>::type; /** * @brief Explicit cast between different quantity types @@ -73,15 +68,29 @@ template constexpr Magnitude auto num = numerator(c_mag); constexpr Magnitude auto den = denominator(c_mag); constexpr Magnitude auto irr = c_mag * (den / num); - using c_rep_type = decltype(common_rep_type(q, To{})); + using c_rep_type = maybe_common_type::rep, typename To::rep>; using c_mag_type = common_magnitude_type; - using multiplier_type = - conditional, std::common_type_t, c_mag_type>; + using multiplier_type = conditional, + // ensure that the multiplier is also floating-point + conditional>, + // reuse user's type if possible + std::common_type_t>, + std::common_type_t>, + c_mag_type>; + using c_type = maybe_common_type; constexpr auto val = [](Magnitude auto m) { return get_value(m); }; - return {static_cast( - static_cast(std::forward(q).numerical_value_is_an_implementation_detail_) * val(num) / - val(den) * val(irr)), - To::reference}; + if constexpr (std::is_floating_point_v) { + // this results in great assembly + constexpr auto ratio = val(num) / val(den) * val(irr); + auto res = static_cast( + static_cast(q.numerical_value_is_an_implementation_detail_) * ratio); + return {res, To::reference}; + } else { + // this is slower but allows conversions like 2000 m -> 2 km without loosing data + auto res = static_cast( + static_cast(q.numerical_value_is_an_implementation_detail_) * val(num) / val(den) * val(irr)); + return {res, To::reference}; + } } } From 3a6249fc5ef77d40da3891d3cc824e5f63afc33d Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 19 Jan 2024 19:34:06 +0100 Subject: [PATCH 12/18] fix: explicit object parameter support fixed --- src/core/include/mp-units/quantity_spec.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/core/include/mp-units/quantity_spec.h b/src/core/include/mp-units/quantity_spec.h index 556b8cd2..ae958d04 100644 --- a/src/core/include/mp-units/quantity_spec.h +++ b/src/core/include/mp-units/quantity_spec.h @@ -411,7 +411,11 @@ struct quantity_spec : quantity_spec { */ template struct derived_quantity_spec : +#ifdef __cpp_explicit_this_parameter + detail::quantity_spec_interface, +#else detail::quantity_spec_interface>, +#endif detail::expr_fractions { using _base_ = detail::expr_fractions; @@ -443,9 +447,9 @@ concept QuantitySpecWithNoSpecifiers = detail::NamedQuantitySpec || detail::I } // namespace detail #ifdef __cpp_explicit_this_parameter -template - requires(get_kind(Q) == Q) -struct kind_of_ : Q { +template + requires(detail::QuantitySpecWithNoSpecifiers>) && (get_kind(Q) == Q) +struct kind_of_ : std::remove_const_t { static constexpr auto _quantity_spec_ = Q; }; #else @@ -1416,12 +1420,9 @@ template template [[nodiscard]] consteval auto remove_kind(Q q) { - if constexpr (detail::QuantityKindSpec) { - if constexpr (requires { Q::_parent_; }) - return Q::_parent_; - else - return Q::_equation_; - } else + if constexpr (detail::QuantityKindSpec) + return Q::_quantity_spec_; + else return q; } From e51a5f6e8eb1de09732e914abce1821eea06d1ad Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 19 Jan 2024 19:38:51 +0100 Subject: [PATCH 13/18] docs: C++23 extensions compiler support updated --- docs/getting_started/installation_and_usage.md | 2 +- docs/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index b2c820a4..9c054aaa 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -25,7 +25,7 @@ specific feature: | **Minimum support** | 12 | 16 | 15 | None | | **`std::format`** | None | None | None | None | | **C++ modules** | None | 17 | None | None | -| **C++23 extensions** | None | None | None | None | +| **C++23 extensions** | 14 | 18 | None | None | More requirements for C++ modules support can be found in the [CMake's documentation](https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html). diff --git a/docs/index.md b/docs/index.md index cdb5ae99..753ba7a2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,7 +33,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units) | **Minimum support** | 12 | 16 | 15 | None | | **`std::format`** | None | None | None | None | | **C++ modules** | None | 17 | None | None | - | **C++23 extensions** | None | None | None | None | + | **C++23 extensions** | 14 | 18 | None | None | More requirements for C++ modules support can be found in the [CMake's documentation](https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html). From 1df2cc7b58793d6f5972f65c5f042774fbacee06 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 19 Jan 2024 21:20:33 +0100 Subject: [PATCH 14/18] feat: do not print space between a number and `percent` or `per_mille` --- src/core/include/mp-units/unit.h | 5 +++++ test/runtime/fmt_test.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index 79b2512a..cbcdde91 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -836,6 +836,11 @@ constexpr Out unit_symbol_impl(Out out, const derived_unit&, unit_symbo template inline constexpr bool space_before_unit_symbol = true; +template<> +inline constexpr bool space_before_unit_symbol = false; +template<> +inline constexpr bool space_before_unit_symbol = false; + template Out, Unit U> constexpr Out unit_symbol_to(Out out, U u, unit_symbol_formatting fmt = unit_symbol_formatting{}) { diff --git a/test/runtime/fmt_test.cpp b/test/runtime/fmt_test.cpp index 71ea8588..b1d131ec 100644 --- a/test/runtime/fmt_test.cpp +++ b/test/runtime/fmt_test.cpp @@ -233,11 +233,11 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = value_cast(15. * isq::length[m] / (100. * isq::length[m])); os << q; - SECTION("iostream") { CHECK(os.str() == "15 %"); } + SECTION("iostream") { CHECK(os.str() == "15%"); } SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "15 %"); } } SECTION("radians") From a62f061028d08a026a673ee8a42d1e80f5bda1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Neboj=C5=A1a=20Cvetkovi=C4=87?= Date: Sat, 20 Jan 2024 00:05:54 +0000 Subject: [PATCH 15/18] feat: `ppm` parts per million Co-authored-by: Mateusz Pusz --- docs/users_guide/framework_basics/dimensionless_quantities.md | 2 ++ src/core/include/mp-units/unit.h | 2 ++ test/static/unit_symbol_test.cpp | 1 + 3 files changed, 5 insertions(+) diff --git a/docs/users_guide/framework_basics/dimensionless_quantities.md b/docs/users_guide/framework_basics/dimensionless_quantities.md index 58949800..7215eb4e 100644 --- a/docs/users_guide/framework_basics/dimensionless_quantities.md +++ b/docs/users_guide/framework_basics/dimensionless_quantities.md @@ -160,6 +160,8 @@ with dimensionless quantities: ```cpp inline constexpr struct percent : named_unit<"%", mag * one> {} percent; inline constexpr struct per_mille : named_unit * one> {} per_mille; +inline constexpr struct parts_per_million : named_unit<"ppm", mag * one> {} parts_per_million; +inline constexpr auto ppm = parts_per_million; ``` diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index cbcdde91..7c210cd2 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -605,6 +605,8 @@ template // clang-format off inline constexpr struct percent : named_unit<"%", mag * one> {} percent; inline constexpr struct per_mille : named_unit * one> {} per_mille; +inline constexpr struct parts_per_million : named_unit<"ppm", mag * one> {} parts_per_million; +inline constexpr auto ppm = parts_per_million; // clang-format on diff --git a/test/static/unit_symbol_test.cpp b/test/static/unit_symbol_test.cpp index 24fe56fa..d52a2f8f 100644 --- a/test/static/unit_symbol_test.cpp +++ b/test/static/unit_symbol_test.cpp @@ -120,6 +120,7 @@ static_assert(unit_symbol(one) == ""); static_assert(unit_symbol(percent) == "%"); static_assert(unit_symbol(per_mille) == "‰"); static_assert(unit_symbol(per_mille) == "%o"); +static_assert(unit_symbol(parts_per_million) == "ppm"); static_assert(unit_symbol(square(metre)) == "m²"); static_assert(unit_symbol(square(metre)) == "m^2"); static_assert(unit_symbol(cubic(metre)) == "m³"); From 057d659c1ff0b465f5a8bb635a36a3d38a1f4699 Mon Sep 17 00:00:00 2001 From: Nebojsa Cvetkovic Date: Sat, 20 Jan 2024 18:21:56 +0000 Subject: [PATCH 16/18] feat: `atan2` 2-argument arctangent --- .../framework_basics/quantity_arithmetics.md | 2 +- .../include/mp-units/systems/angular/math.h | 20 +++++++++++ .../include/mp-units/systems/si/math.h | 21 +++++++++++ test/runtime/math_test.cpp | 36 +++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/docs/users_guide/framework_basics/quantity_arithmetics.md b/docs/users_guide/framework_basics/quantity_arithmetics.md index ceb35262..4795baba 100644 --- a/docs/users_guide/framework_basics/quantity_arithmetics.md +++ b/docs/users_guide/framework_basics/quantity_arithmetics.md @@ -343,7 +343,7 @@ Among others, we can find there the following: - `inverse()`, - `hypot()`, - `sin()`, `cos()`, `tan()`, -- `asin()`, `acos()`, `atan()`. +- `asin()`, `acos()`, `atan()`, `atan2()`. In the library, we can also find _mp-units/random.h_ header file with all the pseudo-random number generators working on quantity types. diff --git a/src/systems/include/mp-units/systems/angular/math.h b/src/systems/include/mp-units/systems/angular/math.h index f2954f05..bbfaa033 100644 --- a/src/systems/include/mp-units/systems/angular/math.h +++ b/src/systems/include/mp-units/systems/angular/math.h @@ -119,4 +119,24 @@ template auto R, typename Rep> return quantity{atan(q.numerical_value_in(one)), radian}; } +template + requires requires(Rep1 v1, Rep2 v2) { + common_reference(R1, R2); + requires requires { atan2(v1, v2); } || requires { std::atan2(v1, v2); }; + } +[[nodiscard]] inline QuantityOf auto atan2(const quantity& y, const quantity& x) noexcept +{ + constexpr auto ref = common_reference(R1, R2); + constexpr auto unit = get_unit(ref); + using std::atan2; + if constexpr (!treat_as_floating_point || !treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(atan2(y.force_numerical_value_in(unit), x.force_numerical_value_in(unit))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{atan2(value_cast(y).numerical_value_in(unit), value_cast(x).numerical_value_in(unit)), + radian}; + } else + return quantity{atan2(y.numerical_value_in(unit), x.numerical_value_in(unit)), radian}; +} + } // namespace mp_units::angular diff --git a/src/systems/include/mp-units/systems/si/math.h b/src/systems/include/mp-units/systems/si/math.h index a2f5971f..eafddcbf 100644 --- a/src/systems/include/mp-units/systems/si/math.h +++ b/src/systems/include/mp-units/systems/si/math.h @@ -120,4 +120,25 @@ template auto R, typename Rep> return quantity{atan(q.numerical_value_in(one)), radian}; } +template + requires requires(Rep1 v1, Rep2 v2) { + common_reference(R1, R2); + requires requires { atan2(v1, v2); } || requires { std::atan2(v1, v2); }; + } +[[nodiscard]] inline QuantityOf auto atan2(const quantity& y, + const quantity& x) noexcept +{ + constexpr auto ref = common_reference(R1, R2); + constexpr auto unit = get_unit(ref); + using std::atan2; + if constexpr (!treat_as_floating_point || !treat_as_floating_point) { + // check what is the return type when called with the integral value + using rep = decltype(atan2(y.force_numerical_value_in(unit), x.force_numerical_value_in(unit))); + // use this type ahead of calling the function to prevent narrowing if a unit conversion is needed + return quantity{atan2(value_cast(y).numerical_value_in(unit), value_cast(x).numerical_value_in(unit)), + radian}; + } else + return quantity{atan2(y.numerical_value_in(unit), x.numerical_value_in(unit)), radian}; +} + } // namespace mp_units::si diff --git a/test/runtime/math_test.cpp b/test/runtime/math_test.cpp index c4e6b759..643f8378 100644 --- a/test/runtime/math_test.cpp +++ b/test/runtime/math_test.cpp @@ -375,6 +375,22 @@ TEST_CASE("SI inverse trigonometric functions", "[inv trig][si]") } } +TEST_CASE("SI atan2 functions", "[atan2][si]") +{ + SECTION("atan2 should work on the same quantities") + { + REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * deg)); + REQUIRE_THAT(si::atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * deg)); + REQUIRE_THAT(si::atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * deg)); + } + SECTION("atan2 should work with different units of the same dimension") + { + REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * deg)); + REQUIRE_THAT(si::atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * deg)); + REQUIRE_THAT(si::atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * deg)); + } +} + TEST_CASE("Angle trigonometric functions", "[trig][angle]") { @@ -449,3 +465,23 @@ TEST_CASE("Angle inverse trigonometric functions", "[inv trig][angle]") REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg])); } } + +TEST_CASE("Angle atan2 functions", "[atan2][angle]") +{ + using namespace mp_units::angular; + using namespace mp_units::angular::unit_symbols; + using mp_units::angular::unit_symbols::deg; + + SECTION("atan2 should work on the same quantities") + { + REQUIRE_THAT(atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * angle[deg])); + REQUIRE_THAT(atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * angle[deg])); + REQUIRE_THAT(atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * angle[deg])); + } + SECTION("atan2 should work with different units of the same dimension") + { + REQUIRE_THAT(atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * angle[deg])); + REQUIRE_THAT(atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * angle[deg])); + REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg])); + } +} From 35be0b7ad0e245cd4f9218dcfc635a593e9d14a2 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 21 Jan 2024 08:20:03 +0100 Subject: [PATCH 17/18] ci: "Check CI" renamed to "Formatting CI" --- .github/workflows/{ci-check.yml => ci-formatting.yml} | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{ci-check.yml => ci-formatting.yml} (98%) diff --git a/.github/workflows/ci-check.yml b/.github/workflows/ci-formatting.yml similarity index 98% rename from .github/workflows/ci-check.yml rename to .github/workflows/ci-formatting.yml index 52e13e6a..84d90c92 100644 --- a/.github/workflows/ci-check.yml +++ b/.github/workflows/ci-formatting.yml @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -name: Check CI +name: Formatting CI on: [push, pull_request] diff --git a/README.md b/README.md index f6e8e764..47a2d2f4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster) [![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster) -[![Check CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-check.yml?branch=master&label=Check%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Check%20CI%22+branch%3Amaster) +[![Formatting CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-formatting.yml?branch=master&label=Formatting%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Formatting%20CI%22+branch%3Amaster) [![GitHub Workflow Documentation](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/documentation.yml?branch=master&label=Documentation)](https://github.com/mpusz/mp-units/actions?query=workflow%3ADocumentation+branch%3Amaster) [![Conan stable](https://img.shields.io/conan/v/mp-units?label=ConanCenter&color=blue)](https://conan.io/center/mp-units) From 64c351906df34809fd3a7b9583f066288dcabe87 Mon Sep 17 00:00:00 2001 From: Nebojsa Cvetkovic Date: Mon, 22 Jan 2024 00:17:57 +0000 Subject: [PATCH 18/18] feat: `fmod` floating-point division remainder --- .../framework_basics/quantity_arithmetics.md | 2 +- src/core/include/mp-units/math.h | 18 ++++++++++++++++++ test/runtime/math_test.cpp | 18 ++++++++++++++++++ test/static/math_test.cpp | 6 ++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/users_guide/framework_basics/quantity_arithmetics.md b/docs/users_guide/framework_basics/quantity_arithmetics.md index 4795baba..62a487cc 100644 --- a/docs/users_guide/framework_basics/quantity_arithmetics.md +++ b/docs/users_guide/framework_basics/quantity_arithmetics.md @@ -337,7 +337,7 @@ Among others, we can find there the following: - `exp()`, - `abs()`, - `epsilon()`, -- `fma()`, +- `fma()`, `fmod()`, - `isfinite()`, `isinf()`, `isnan()`, - `floor()`, `ceil()`, `round()`, - `inverse()`, diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index 2ce773be..ac9ecae9 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -198,6 +198,24 @@ template } +/** + * @brief Computes the floating-point remainder of the division operation x / y. + */ +template + requires requires(Rep1 v1, Rep2 v2) { + common_reference(R1, R2); + requires requires { fmod(v1, v2); } || requires { std::fmod(v1, v2); }; + } +[[nodiscard]] constexpr QuantityOf auto fmod(const quantity& x, + const quantity& y) noexcept +{ + constexpr auto ref = common_reference(R1, R2); + constexpr auto unit = get_unit(ref); + using std::fmod; + return quantity{fmod(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref}; +} + + /** * @brief Returns the epsilon of the quantity * diff --git a/test/runtime/math_test.cpp b/test/runtime/math_test.cpp index 643f8378..3671ebf8 100644 --- a/test/runtime/math_test.cpp +++ b/test/runtime/math_test.cpp @@ -71,6 +71,24 @@ TEST_CASE("'fma()' on quantity changes the value and the dimension accordingly", REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m)); } +TEST_CASE("fmod functions", "[math][fmod]") +{ + SECTION("fmod should work on the same quantities") + { + REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]); + REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]); + REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]); + REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]); + } + SECTION("fmod should work with different units of the same dimension") + { + REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]); + REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]); + REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]); + REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]); + } +} + TEST_CASE("'isfinite()' accepts dimensioned arguments", "[math][isfinite]") { REQUIRE(isfinite(4.0 * isq::length[m])); } TEST_CASE("'isinf()' accepts dimensioned arguments", "[math][isinf]") { REQUIRE(!isinf(4.0 * isq::length[m])); } diff --git a/test/static/math_test.cpp b/test/static/math_test.cpp index d4f52037..1f4e10f3 100644 --- a/test/static/math_test.cpp +++ b/test/static/math_test.cpp @@ -57,6 +57,12 @@ static_assert(compare(fma(2.0 * one, 3.0 * m, 1.0 * m), 7.0 * m)); static_assert(compare(fma(2.0 * m, 3.0 * one, 1.0 * m), 7.0 * m)); static_assert(compare(fma(2 * m, 3.0f * m, 1.0 * m2), 7.0 * m2)); static_assert(compare(fma(isq::width(2.0 * m), 2.0 * one, isq::height(3.0 * m)), isq::length(7.0 * m))); +static_assert(compare(fmod(4.0 * km, 3.0 * km), 1.0 * km)); +static_assert(compare(fmod(-4.0 * km, 3.0 * km), -1.0 * km)); +static_assert(compare(fmod(9.0 * km, -3.0 * km), 0.0 * km)); +static_assert(compare(fmod(9.5 * km, -2000 * m), 1500.0 * m)); +static_assert(compare(fmod(3 * km, 2 * km), 1.0 * km)); +static_assert(compare(fmod(4 * km, 2.5f * km), 1.5 * km)); static_assert(compare(pow<0>(2 * m), 1 * one)); static_assert(compare(pow<1>(2 * m), 2 * m)); static_assert(compare(pow<2>(2 * m), 4 * pow<2>(m), 4 * m2));