From cf3408a3c890562c4e11608792424a53c59a2614 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 2 Feb 2023 14:56:29 +0100 Subject: [PATCH] refactor: implementation cleanup + support for units as references started --- example/avg_speed.cpp | 7 +- example/box_example.cpp | 12 +- .../glide_computer/include/glide_computer.h | 2 +- example/kalman_filter/kalman.h | 14 +- .../kalman_filter/kalman_filter-example_1.cpp | 2 +- .../kalman_filter/kalman_filter-example_5.cpp | 4 +- .../kalman_filter/kalman_filter-example_6.cpp | 4 +- .../kalman_filter/kalman_filter-example_7.cpp | 4 +- .../kalman_filter/kalman_filter-example_8.cpp | 4 +- src/core-fmt/include/mp_units/format.h | 6 +- src/core-io/include/mp_units/quantity_io.h | 4 +- src/core/CMakeLists.txt | 26 +++ .../mp_units/bits/dimension_concepts.h | 110 +++++++++ .../include/mp_units/bits/quantity_cast.h | 40 +--- .../include/mp_units/bits/quantity_concepts.h | 211 +++--------------- .../mp_units/bits/quantity_point_concepts.h | 124 ++++++++++ .../mp_units/bits/quantity_spec_concepts.h | 137 ++++++++++++ .../mp_units/bits/reference_concepts.h | 51 +++++ .../mp_units/bits/representation_concepts.h | 80 +++++++ .../include/mp_units/bits/unit_concepts.h | 159 +++++++++++++ src/core/include/mp_units/concepts.h | 31 +++ src/core/include/mp_units/dimension.h | 71 +----- src/core/include/mp_units/quantity.h | 94 ++++---- src/core/include/mp_units/quantity_point.h | 38 ++-- src/core/include/mp_units/reference.h | 148 ++++++++++-- src/core/include/mp_units/system_reference.h | 2 +- src/core/include/mp_units/unit.h | 200 ++++------------- src/utility/include/mp_units/chrono.h | 5 +- src/utility/include/mp_units/math.h | 94 ++++---- test/unit_test/static/dimension_test.cpp | 8 +- test/unit_test/static/unit_test.cpp | 8 - 31 files changed, 1085 insertions(+), 615 deletions(-) create mode 100644 src/core/include/mp_units/bits/dimension_concepts.h create mode 100644 src/core/include/mp_units/bits/quantity_point_concepts.h create mode 100644 src/core/include/mp_units/bits/quantity_spec_concepts.h create mode 100644 src/core/include/mp_units/bits/reference_concepts.h create mode 100644 src/core/include/mp_units/bits/representation_concepts.h create mode 100644 src/core/include/mp_units/bits/unit_concepts.h create mode 100644 src/core/include/mp_units/concepts.h diff --git a/example/avg_speed.cpp b/example/avg_speed.cpp index a22d8537..8b39b06c 100644 --- a/example/avg_speed.cpp +++ b/example/avg_speed.cpp @@ -44,15 +44,16 @@ constexpr quantity fixed_double_si_avg_speed(quantity auto avg_speed(quantity_of auto d, quantity_of auto t) +constexpr QuantityOf auto avg_speed(QuantityOf auto d, QuantityOf auto t) { return quantity_cast(d / t); } -template D, quantity_of T, quantity_of V> +template D, QuantityOf T, QuantityOf V> void print_result(D distance, T duration, V speed) { - const auto result_in_kmph = quantity_cast(speed); + constexpr auto kmph = si::kilo / si::hour; + const auto result_in_kmph = quantity_cast(speed); std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph << ".\n"; } diff --git a/example/box_example.cpp b/example/box_example.cpp index c1c6ffa8..0a005da9 100644 --- a/example/box_example.cpp +++ b/example/box_example.cpp @@ -53,10 +53,10 @@ public: { } - [[nodiscard]] constexpr quantity_of auto filled_weight() const + [[nodiscard]] constexpr QuantityOf auto filled_weight() const { - const weak_quantity_of auto volume = base_ * height_; - const weak_quantity_of auto mass = density_ * volume; + const WeakQuantityOf auto volume = base_ * height_; + const WeakQuantityOf auto mass = density_ * volume; return quantity_cast(mass * g); } @@ -96,9 +96,9 @@ int main() const auto spare_capacity = box.spare_capacity(measured_mass); const auto filled_weight = box.filled_weight(); - const weak_quantity_of auto input_flow_rate = measured_mass / fill_time; - const weak_quantity_of auto float_rise_rate = fill_level / fill_time; - const quantity_of auto fill_time_left = (height / fill_level - 1) * fill_time; + const WeakQuantityOf auto input_flow_rate = measured_mass / fill_time; + const WeakQuantityOf auto float_rise_rate = fill_level / fill_time; + const QuantityOf auto fill_time_left = (height / fill_level - 1) * fill_time; const auto fill_percent = (fill_level / height)[percent]; diff --git a/example/glide_computer/include/glide_computer.h b/example/glide_computer/include/glide_computer.h index 74237cee..37599be6 100644 --- a/example/glide_computer/include/glide_computer.h +++ b/example/glide_computer/include/glide_computer.h @@ -105,7 +105,7 @@ struct glider { std::array polar; }; -constexpr mp_units::weak_quantity_of auto glide_ratio(const glider::polar_point& polar) +constexpr mp_units::WeakQuantityOf auto glide_ratio(const glider::polar_point& polar) { return polar.v / -polar.climb; } diff --git a/example/kalman_filter/kalman.h b/example/kalman_filter/kalman.h index fe8a73d8..428130de 100644 --- a/example/kalman_filter/kalman.h +++ b/example/kalman_filter/kalman.h @@ -95,15 +95,15 @@ constexpr mp_units::quantity kalman_gain } // state update -template K> +template K> requires(Q::quantity_spec == QM::quantity_spec) constexpr state state_update(const state& predicted, QM measured, K gain) { return {get<0>(predicted) + gain * (measured - get<0>(predicted))}; } -template K, - mp_units::quantity_of T> +template K, + mp_units::QuantityOf T> requires(Q1::quantity_spec == QM::quantity_spec) constexpr state state_update(const state& predicted, QM measured, std::array gain, T interval) { @@ -113,7 +113,7 @@ constexpr state state_update(const state& predicted, QM measured } template K, mp_units::quantity_of T> + mp_units::QuantityOf K, mp_units::QuantityOf T> requires(Q1::quantity_spec == QM::quantity_spec) constexpr state state_update(const state& predicted, QM measured, std::array gain, T interval) @@ -125,14 +125,14 @@ constexpr state state_update(const state& predicted, QM } // covariance update -template K> +template K> constexpr Q covariance_update(Q uncertainty, K gain) { return (1 - gain) * uncertainty; } // state extrapolation -template T> +template T> constexpr state state_extrapolation(const state& estimated, T interval) { const auto q1 = get<0>(estimated) + get<1>(estimated) * interval; @@ -140,7 +140,7 @@ constexpr state state_extrapolation(const state& estimated, T in return {q1, q2}; } -template T> +template T> constexpr state state_extrapolation(const state& estimated, T interval) { const auto q1 = get<0>(estimated) + get<1>(estimated) * interval + get<2>(estimated) * pow<2>(interval) / 2; diff --git a/example/kalman_filter/kalman_filter-example_1.cpp b/example/kalman_filter/kalman_filter-example_1.cpp index c2a30f1c..9f9e1d54 100644 --- a/example/kalman_filter/kalman_filter-example_1.cpp +++ b/example/kalman_filter/kalman_filter-example_1.cpp @@ -38,7 +38,7 @@ void print_header(const kalman::State auto& initial) "Next Estimate"); } -void print(auto iteration, quantity_of auto gain, Quantity auto measured, +void print(auto iteration, QuantityOf auto gain, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) { std::cout << STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current, next); diff --git a/example/kalman_filter/kalman_filter-example_5.cpp b/example/kalman_filter/kalman_filter-example_5.cpp index 4547a8fa..dc5d14e6 100644 --- a/example/kalman_filter/kalman_filter-example_5.cpp +++ b/example/kalman_filter/kalman_filter-example_5.cpp @@ -40,7 +40,7 @@ void print_header(kalman::estimation initial) "Next Estimate"); } -template K> +template K> void print(auto iteration, K gain, Q measured, kalman::estimation current, kalman::estimation next) { std::cout << STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured, current, @@ -60,7 +60,7 @@ int main() const auto measurement_uncertainty = pow<2>(5. * isq::height[m]); auto update = [=](const estimation& previous, const Q& measurement, - quantity_of auto gain) { + QuantityOf auto gain) { return estimation{state_update(previous.state, measurement, gain), covariance_update(previous.uncertainty, gain)}; }; diff --git a/example/kalman_filter/kalman_filter-example_6.cpp b/example/kalman_filter/kalman_filter-example_6.cpp index 66d5bee7..eda24997 100644 --- a/example/kalman_filter/kalman_filter-example_6.cpp +++ b/example/kalman_filter/kalman_filter-example_6.cpp @@ -41,7 +41,7 @@ void print_header(kalman::estimation initial) "Next Estimate"); } -template K> +template K> void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) { std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, @@ -64,7 +64,7 @@ int main() const auto measurement_uncertainty = pow<2>(0.1 * deg_C); auto update = [=](const estimation& previous, const QP& meassurement, - quantity_of auto gain) { + QuantityOf auto gain) { return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; }; diff --git a/example/kalman_filter/kalman_filter-example_7.cpp b/example/kalman_filter/kalman_filter-example_7.cpp index 3d10edf0..6ebb06e5 100644 --- a/example/kalman_filter/kalman_filter-example_7.cpp +++ b/example/kalman_filter/kalman_filter-example_7.cpp @@ -41,7 +41,7 @@ void print_header(kalman::estimation initial) "Next Estimate"); } -template K> +template K> void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) { std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, @@ -64,7 +64,7 @@ int main() const auto measurement_uncertainty = pow<2>(0.1 * deg_C); auto update = [=](const estimation& previous, const QP& meassurement, - quantity_of auto gain) { + QuantityOf auto gain) { return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; }; diff --git a/example/kalman_filter/kalman_filter-example_8.cpp b/example/kalman_filter/kalman_filter-example_8.cpp index a5b9d83d..ea2606a7 100644 --- a/example/kalman_filter/kalman_filter-example_8.cpp +++ b/example/kalman_filter/kalman_filter-example_8.cpp @@ -41,7 +41,7 @@ void print_header(kalman::estimation initial) "Next Estimate"); } -template K> +template K> void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) { std::cout << STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain, @@ -64,7 +64,7 @@ int main() const auto measurement_uncertainty = pow<2>(0.1 * deg_C); auto update = [=](const estimation& previous, const QP& meassurement, - quantity_of auto gain) { + QuantityOf auto gain) { return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; }; diff --git a/src/core-fmt/include/mp_units/format.h b/src/core-fmt/include/mp_units/format.h index d956fac9..8216eeb5 100644 --- a/src/core-fmt/include/mp_units/format.h +++ b/src/core-fmt/include/mp_units/format.h @@ -245,7 +245,7 @@ struct quantity_formatter { template S> void on_quantity_unit(It, S) { - out = unit_symbol_to(out, Reference.unit, specs.unit); + out = unit_symbol_to(out, get_unit(Reference), specs.unit); } }; @@ -396,9 +396,9 @@ private: if (begin == end || *begin == '}') { // default format should print value followed by the unit separated with 1 space out = mp_units::detail::format_units_quantity_value(out, q.number(), specs.rep, ctx.locale()); - if constexpr (!std::derived_from>) { + if constexpr (!std::derived_from>) { *out++ = CharT(' '); - out = unit_symbol_to(out, Reference.unit); + out = unit_symbol_to(out, get_unit(Reference)); } } else { // user provided format diff --git a/src/core-io/include/mp_units/quantity_io.h b/src/core-io/include/mp_units/quantity_io.h index 1aac4cde..dde42238 100644 --- a/src/core-io/include/mp_units/quantity_io.h +++ b/src/core-io/include/mp_units/quantity_io.h @@ -35,9 +35,9 @@ template void to_stream(std::basic_ostream& os, const quantity& q) { os << q.number(); - if constexpr (!std::derived_from>) { + if constexpr (!std::derived_from>) { os << " "; - unit_symbol_to(std::ostream_iterator(os), R.unit); + unit_symbol_to(std::ostream_iterator(os), get_unit(R)); } } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index cbb0fb5f..7248c12c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -33,6 +33,32 @@ check_libcxx_in_use(${projectPrefix}LIBCXX) add_library( mp-units-core INTERFACE + + include/mp_units/bits/external/fixed_string.h + include/mp_units/bits/external/hacks.h + include/mp_units/bits/external/type_list.h + include/mp_units/bits/external/type_name.h + include/mp_units/bits/external/type_traits.h + + include/mp_units/bits/algorithm.h + include/mp_units/bits/dimension_concepts.h + include/mp_units/bits/expression_template.h + include/mp_units/bits/magnitude.h + include/mp_units/bits/math_concepts.h + include/mp_units/bits/prime.h + include/mp_units/bits/quantity_cast.h + include/mp_units/bits/quantity_concepts.h + include/mp_units/bits/quantity_point_concepts.h + include/mp_units/bits/quantity_spec_concepts.h + include/mp_units/bits/ratio_maths.h + include/mp_units/bits/ratio.h + include/mp_units/bits/reference_concepts.h + include/mp_units/bits/representation_concepts.h + include/mp_units/bits/symbol_text.h + include/mp_units/bits/text_tools.h + include/mp_units/bits/unit_concepts.h + + include/mp_units/concepts.h include/mp_units/customization_points.h include/mp_units/dimension.h include/mp_units/quantity.h diff --git a/src/core/include/mp_units/bits/dimension_concepts.h b/src/core/include/mp_units/bits/dimension_concepts.h new file mode 100644 index 00000000..f03bbd8c --- /dev/null +++ b/src/core/include/mp_units/bits/dimension_concepts.h @@ -0,0 +1,110 @@ +// 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 +#include +#include + +namespace mp_units { + +template +struct base_dimension; + +namespace detail { + +template +void to_base_base_dimension(const volatile base_dimension*); + +template +inline constexpr bool is_specialization_of_base_dimension = false; + +template +inline constexpr bool is_specialization_of_base_dimension> = true; + +} // namespace detail + +/** + * @brief A concept matching all named base dimensions in the library. + * + * Satisfied by all dimension types derived from a specialization of `base_dimension`. + */ +template +concept BaseDimension = + requires(T* t) { detail::to_base_base_dimension(t); } && (!detail::is_specialization_of_base_dimension); + +namespace detail { + +template +inline constexpr bool is_dimension_one = false; + +template +inline constexpr bool is_power_of_dim = requires { + requires is_specialization_of_power && (BaseDimension || is_dimension_one); +}; + +template +inline constexpr bool is_per_of_dims = false; + +template +inline constexpr bool is_per_of_dims> = + (... && (BaseDimension || is_dimension_one || is_power_of_dim)); + +} // namespace detail + +template +concept DerivedDimensionExpr = + BaseDimension || detail::is_dimension_one || detail::is_power_of_dim || detail::is_per_of_dims; + +template +struct derived_dimension; + +namespace detail { + +template +void to_base_specialization_of_derived_dimension(const volatile derived_dimension*); + +template +inline constexpr bool is_derived_from_specialization_of_derived_dimension = + requires(T* t) { to_base_specialization_of_derived_dimension(t); }; + +/** + * @brief A concept matching all derived dimensions in the library. + * + * Satisfied by all dimension types either being a specialization of `derived_dimension` + * or derived from it (inheritance needed to properly handle `dimension_one`). + */ +template +concept DerivedDimension = detail::is_derived_from_specialization_of_derived_dimension; + +} // namespace detail + +/** + * @brief A concept matching all dimensions in the library. + * + * Satisfied by all dimension types for which either `BaseDimension` or `DerivedDimension` is `true`. + */ +template +concept Dimension = BaseDimension || detail::DerivedDimension; + +} // namespace mp_units diff --git a/src/core/include/mp_units/bits/quantity_cast.h b/src/core/include/mp_units/bits/quantity_cast.h index 70d5874e..780ed487 100644 --- a/src/core/include/mp_units/bits/quantity_cast.h +++ b/src/core/include/mp_units/bits/quantity_cast.h @@ -36,7 +36,7 @@ UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA namespace mp_units { -template Rep> +template Rep> class quantity; // template U, Representation Rep> @@ -57,12 +57,12 @@ class quantity; */ template requires(interconvertible(To::reference, R)) && - ((R.unit == To::unit && std::constructible_from) || - (R.unit != To::unit)) // && scalable_with_)) + ((get_unit(R) == To::unit && std::constructible_from) || + (get_unit(R) != To::unit)) // && scalable_with_)) // TODO how to constrain the second part here? [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - if constexpr (R.unit == To::unit) { + if constexpr (get_unit(R) == To::unit) { // no scaling of the number needed return To(static_cast(q.number())); // this is the only (and recommended) way to do // a truncating conversion on a number, so we are @@ -91,7 +91,8 @@ template return std::intmax_t{}; }()); - constexpr Magnitude auto c_mag = detail::get_canonical_unit(R.unit).mag / detail::get_canonical_unit(To::unit).mag; + constexpr Magnitude auto c_mag = + detail::get_canonical_unit(get_unit(R)).mag / detail::get_canonical_unit(To::unit).mag; constexpr Magnitude auto num = numerator(c_mag); constexpr Magnitude auto den = denominator(c_mag); constexpr Magnitude auto irr = c_mag * (den / num); @@ -101,26 +102,6 @@ template } } -/** - * @brief Explicit cast of a quantity - * - * Implicit conversions between quantities of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used. - * - * This cast gets a target reference to cast to. For example: - * - * auto v = quantity_cast(q); - * - * @tparam ToR a reference to use for a target quantity - */ -template - requires(interconvertible(ToR, R)) -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) -{ - return quantity_cast>(q); -} - - /** * @brief Explicit cast of a quantity * @@ -134,7 +115,7 @@ template * @tparam ToQS a quantity specification to use for a target quantity */ template - requires(interconvertible(ToQS, R.quantity_spec)) + requires(interconvertible(ToQS, get_quantity_spec(R))) [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { constexpr reference::unit> r; @@ -154,7 +135,7 @@ template * @tparam ToU a unit to use for a target quantity */ template - requires(interconvertible(ToU, R.unit)) + requires(interconvertible(ToU, get_unit(R))) [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { constexpr reference::quantity_spec, ToU> r; @@ -174,7 +155,7 @@ template * @tparam ToRep a representation type to use for a target quantity */ template - requires RepresentationOf && std::constructible_from + requires RepresentationOf && std::constructible_from [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { return quantity_cast>(q); @@ -189,7 +170,8 @@ template // * This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example: // * // * auto q1 = mp_units::quantity_point_cast(quantity_point{1_q_ms}); -// * auto q1 = mp_units::quantity_point_cast>(quantity_point{1_q_ms}); +// * auto q1 = +// mp_units::quantity_point_cast>(quantity_point{1_q_ms}); // * auto q1 = mp_units::quantity_point_cast(quantity_point{200_q_Gal}); // * auto q1 = mp_units::quantity_point_cast(quantity_point{1_q_ms}); // * auto q1 = mp_units::quantity_point_cast(quantity_point{1_q_ms}); diff --git a/src/core/include/mp_units/bits/quantity_concepts.h b/src/core/include/mp_units/bits/quantity_concepts.h index e5b82521..3dcc06bc 100644 --- a/src/core/include/mp_units/bits/quantity_concepts.h +++ b/src/core/include/mp_units/bits/quantity_concepts.h @@ -22,100 +22,14 @@ #pragma once -#include +#include +#include +#include #include -#include -#include namespace mp_units { -/** - * @brief Quantity character - * - * Scalars, vectors and tensors are mathematical objects that can be used to - * denote certain physical quantities and their values. They are as such - * independent of the particular choice of a coordinate system, whereas - * each scalar component of a vector or a tensor and each component vector and - * component tensor depend on that choice. - * - * A scalar is a physical quantity that has magnitude but no direction. - * - * Vectors are physical quantities that possess both magnitude and direction - * and whose operations obey the axioms of a vector space. - * - * Tensors can be used to describe more general physical quantities. - * For example, the Cauchy stress tensor possess magnitude, direction, - * and orientation qualities. - */ -enum class quantity_character { scalar, vector, tensor }; - -namespace detail { - -template -inline constexpr bool is_specialization_of_derived_quantity_spec = false; - -} - -/** - * @brief Concept matching quantity specification types - * - * Satisfied by all `derived_quantity_spec` specializations. - */ -template -concept DerivedQuantitySpec = detail::is_specialization_of_derived_quantity_spec; - -template -concept QuantitySpec = NamedQuantitySpec || DerivedQuantitySpec; - -template -struct reference; - -namespace detail { - -template -inline constexpr bool is_specialization_of_reference = false; - -template -inline constexpr bool is_specialization_of_reference> = true; - -} // namespace detail - -/** - * @brief A concept matching all references in the library. - * - * Satisfied by all specializations of @c reference. - */ -template -concept Reference = detail::is_specialization_of_reference; - -template -concept common_type_with_ = // exposition only - (std::same_as, std::common_type_t>) && - (std::constructible_from, T>) && (std::constructible_from, U>); - -template -concept scalable_number_ = // exposition only - (std::regular_invocable, T, U>) && (std::regular_invocable, T, U>); - -template -concept castable_number_ = // exposition only - common_type_with_ && scalable_number_>; - -// TODO Fix it according to quantity_cast implementation -template -concept scalable_ = // exposition only - castable_number_ || (requires { typename T::value_type; } && castable_number_ && - scalable_number_>); - -template -concept Representation = (is_scalar || is_vector || is_tensor) && std::regular && scalable_; - -template -concept RepresentationOf = Representation && ((Ch == quantity_character::scalar && is_scalar) || - (Ch == quantity_character::vector && is_vector) || - (Ch == quantity_character::tensor && is_tensor)); - -template Rep> +template Rep> class quantity; namespace detail { @@ -125,18 +39,24 @@ void to_base_specialization_of_quantity(const volatile quantity*); } // namespace detail +/** + * @brief A concept matching all quantities in the library + * + * Satisfied by all types being a either specialization or derived from `quantity` + */ template concept Quantity = requires(T* t) { detail::to_base_specialization_of_quantity(t); }; -namespace detail { - -template -[[nodiscard]] consteval bool is_kind_of(Q1, Q2) -{ - return std::derived_from; -} - -} // namespace detail +/** + * @brief A concept matching all quantities with provided dimension or reference + * + * Satisfied by all quantities with a dimension/reference being the instantiation derived from + * the provided dimension/reference type. + */ +template +concept QuantityOf = Quantity && ((Dimension> && Q::dimension == V) || + (QuantitySpec> && Q::quantity_spec == V) || + (Reference> && Q::reference == V)); /** * @brief A concept matching all quantities with provided dimension or reference @@ -145,102 +65,27 @@ template * the provided dimension/reference type. */ template -concept quantity_of = Quantity && - ((Dimension> && Q::dimension == V) || - (QuantitySpec> && detail::is_kind_of(Q::quantity_spec, V)) || - (Reference> && Q::reference == V)); +concept WeakQuantityOf = + Quantity && ((Dimension> && Q::dimension == V) || + (QuantitySpec> && interconvertible(Q::quantity_spec, V)) || + (Reference> && Q::dimension == V.dimension && Q::unit == V.unit)); /** - * @brief A concept matching all quantities with provided dimension or reference + * @brief A concept matching all external quantities like types * - * Satisfied by all quantities with a dimension/reference being the instantiation derived from - * the provided dimension/reference type. + * Satisfied by all external types (not-defined in mp-units) that via a `quantity_like_traits` provide + * all quantity-specific information. */ -template -concept weak_quantity_of = Quantity && - ((Dimension> && Q::dimension == V) || - (QuantitySpec> && interconvertible(Q::quantity_spec, V)) || - (Reference> && Q::dimension == V.dimension && - Q::unit == V.unit)); - template -concept quantity_like = requires(T q) { +concept QuantityLike = requires(T q) { quantity_like_traits::reference; typename quantity_like_traits::rep; requires Reference::reference)>>; requires RepresentationOf::rep, - quantity_like_traits::reference.quantity_spec.character>; + get_quantity_spec(quantity_like_traits::reference).character>; { quantity_like_traits::number(q) } -> std::convertible_to::rep>; }; - -template -struct absolute_point_origin { - static constexpr QuantitySpec auto quantity_spec = Q; -}; - -namespace detail { - -template -inline constexpr bool is_quantity_point = false; - -template -void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin*); - -template -inline constexpr bool is_derived_from_specialization_of_absolute_point_origin = - requires(T * t) { to_base_specialization_of_absolute_point_origin(t); }; - -} // namespace detail - -template -concept QuantityPoint = detail::is_quantity_point; - -template -concept point_origin = QuantityPoint || detail::is_derived_from_specialization_of_absolute_point_origin; - -template -concept point_origin_for = point_origin && QuantitySpec> && - detail::is_kind_of(Q, T::quantity_spec); - -template auto PO, RepresentationOf Rep> -class quantity_point; - -namespace detail { - -template -void to_base_specialization_of_quantity_point(const volatile quantity_point*); - -template - requires requires(T* t) { detail::to_base_specialization_of_quantity_point(t); } -inline constexpr bool is_quantity_point = true; - -} // namespace detail - -template -concept quantity_point_of = - QuantityPoint && - ((Dimension> && QP::dimension == V) || - (QuantitySpec> && detail::is_kind_of(QP::quantity_spec, V)) || - (Reference> && QP::reference == V) || - (point_origin> && - std::same_as, std::remove_const_t>)); - -template -concept quantity_point_like = requires(T q) { - quantity_point_like_traits::reference; - quantity_point_like_traits::point_origin; - typename quantity_point_like_traits::rep; - requires Reference::reference)>>; - requires point_origin::point_origin)>>; - requires RepresentationOf::rep, - quantity_point_like_traits::reference.quantity_spec.character>; - requires std::constructible_from< - typename quantity_point::reference, quantity_point_like_traits::point_origin, - typename quantity_point_like_traits::rep>::quantity_type, - decltype(quantity_point_like_traits::relative(q))>; -}; - } // namespace mp_units diff --git a/src/core/include/mp_units/bits/quantity_point_concepts.h b/src/core/include/mp_units/bits/quantity_point_concepts.h new file mode 100644 index 00000000..fd18ea65 --- /dev/null +++ b/src/core/include/mp_units/bits/quantity_point_concepts.h @@ -0,0 +1,124 @@ +// 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 +#include +#include +#include + +namespace mp_units { + +template +struct absolute_point_origin; + +namespace detail { + +template +inline constexpr bool is_quantity_point = false; + +template +void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin*); + +template +inline constexpr bool is_derived_from_specialization_of_absolute_point_origin = + requires(T* t) { to_base_specialization_of_absolute_point_origin(t); }; + +} // namespace detail + +/** + * @brief A concept matching all quantity points in the library + * + * Satisfied by all types being a either specialization or derived from `quantity_point` + */ +template +concept QuantityPoint = detail::is_quantity_point; + +/** + * @brief A concept matching all quantity point origins in the library + * + * Satisfied by either quantity points or by all types derived from `absolute_point_origin` class template. + */ +template +concept PointOrigin = QuantityPoint || detail::is_derived_from_specialization_of_absolute_point_origin; + +/** + * @brief A concept matching all quantity point origins for a specified quantity type in the library + * + * Satisfied by all quantity point origins that are defined using a provided quantity specification. + */ +template +concept PointOriginFor = PointOrigin && QuantitySpec> && Q == T::quantity_spec; + +template auto PO, + RepresentationOf Rep> +class quantity_point; + +namespace detail { + +template +void to_base_specialization_of_quantity_point(const volatile quantity_point*); + +template + requires requires(T* t) { detail::to_base_specialization_of_quantity_point(t); } +inline constexpr bool is_quantity_point = true; + +} // namespace detail + +/** + * @brief A concept matching all quantity points with provided dimension or reference + * + * Satisfied by all quantity points with a dimension/reference being the instantiation derived from + * the provided dimension/reference type. + */ +template +concept QuantityPointOf = + QuantityPoint && + ((Dimension> && QP::dimension == V) || + (QuantitySpec> && QP::quantity_spec == V) || + (Reference> && QP::reference == V) || + (PointOrigin> && + std::same_as, std::remove_const_t>)); + +/** + * @brief A concept matching all external quantity point like types + * + * Satisfied by all external types (not-defined in mp-units) that via a `quantity_point_like_traits` provide + * all quantity_point-specific information. + */ +template +concept QuantityPointLike = requires(T q) { + quantity_point_like_traits::reference; + quantity_point_like_traits::point_origin; + typename quantity_point_like_traits::rep; + requires Reference::reference)>>; + requires PointOrigin::point_origin)>>; + requires RepresentationOf::rep, + get_quantity_spec(quantity_point_like_traits::reference).character>; + requires std::constructible_from< + typename quantity_point::reference, quantity_point_like_traits::point_origin, + typename quantity_point_like_traits::rep>::quantity_type, + decltype(quantity_point_like_traits::relative(q))>; +}; + +} // namespace mp_units diff --git a/src/core/include/mp_units/bits/quantity_spec_concepts.h b/src/core/include/mp_units/bits/quantity_spec_concepts.h new file mode 100644 index 00000000..503f89a4 --- /dev/null +++ b/src/core/include/mp_units/bits/quantity_spec_concepts.h @@ -0,0 +1,137 @@ +// 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 +#include + +namespace mp_units { + +#ifdef __cpp_explicit_this_parameter +template +#else +template +#endif +struct quantity_spec; + +namespace detail { + +#ifdef __cpp_explicit_this_parameter +template +void to_base_specialization_of_quantity_spec(const volatile quantity_spec*); +#else +template +void to_base_specialization_of_quantity_spec(const volatile quantity_spec*); +#endif + +#ifdef __cpp_explicit_this_parameter +template +template +void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec*); +#else +template +void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec*); +#endif + +template +inline constexpr bool is_specialization_of_quantity_spec = false; + +#ifdef __cpp_explicit_this_parameter +template +inline constexpr bool is_specialization_of_quantity_spec> = true; +#else +template +inline constexpr bool is_specialization_of_quantity_spec> = true; +#endif + +/** + * @brief Concept matching all named quantity specification types + * + * Satisfied by all types that derive from `quantity_spec`. + */ +template +concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_quantity_spec(t); } && + (!detail::is_specialization_of_quantity_spec); + +} // namespace detail + +/** + * @brief Concept matching all named base quantity specification types + * + * Satisfied by all types that derive from `quantity_spec` taking a base dimension + * as a template parameter. + */ +template +concept BaseQuantitySpec = + detail::NamedQuantitySpec && requires(T* t) { detail::to_base_specialization_of_base_quantity_spec(t); }; + +namespace detail { + +template +inline constexpr bool is_dimensionless = false; + +template +inline constexpr bool is_power_of_quantity_spec = requires { + requires is_specialization_of_power && + (NamedQuantitySpec || is_dimensionless); +}; + +template +inline constexpr bool is_per_of_quantity_specs = false; + +template +inline constexpr bool is_per_of_quantity_specs> = + (... && (NamedQuantitySpec || is_dimensionless || is_power_of_quantity_spec)); + +} // namespace detail + +template +concept DerivedQuantitySpecExpr = detail::NamedQuantitySpec || detail::is_dimensionless || + detail::is_power_of_quantity_spec || detail::is_per_of_quantity_specs; + + +template +struct derived_quantity_spec; + +namespace detail { + +template +inline constexpr bool is_derived_from_specialization_of_derived_quantity_spec = false; + +template +inline constexpr bool is_derived_from_specialization_of_derived_quantity_spec> = true; + +/** + * @brief Concept matching all derived quantity specification types + * + * Satisfied by all `derived_quantity_spec` specializations. + */ +template +concept DerivedQuantitySpec = detail::is_derived_from_specialization_of_derived_quantity_spec; + +} // namespace detail + +template +concept QuantitySpec = detail::NamedQuantitySpec || detail::DerivedQuantitySpec; + +} // namespace mp_units diff --git a/src/core/include/mp_units/bits/reference_concepts.h b/src/core/include/mp_units/bits/reference_concepts.h new file mode 100644 index 00000000..669342ff --- /dev/null +++ b/src/core/include/mp_units/bits/reference_concepts.h @@ -0,0 +1,51 @@ +// 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 +#include + +namespace mp_units { + +template +struct reference; + +namespace detail { + +template +inline constexpr bool is_specialization_of_reference = false; + +template +inline constexpr bool is_specialization_of_reference> = true; + +} // namespace detail + +/** + * @brief A concept matching all references in the library. + * + * Satisfied by all specializations of @c reference. + */ +template +concept Reference = AssociatedUnit || detail::is_specialization_of_reference; + +} // namespace mp_units diff --git a/src/core/include/mp_units/bits/representation_concepts.h b/src/core/include/mp_units/bits/representation_concepts.h new file mode 100644 index 00000000..aba35c59 --- /dev/null +++ b/src/core/include/mp_units/bits/representation_concepts.h @@ -0,0 +1,80 @@ +// 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 +#include +#include +#include +#include + +namespace mp_units { + +/** + * @brief Quantity character + * + * Scalars, vectors and tensors are mathematical objects that can be used to + * denote certain physical quantities and their values. They are as such + * independent of the particular choice of a coordinate system, whereas + * each scalar component of a vector or a tensor and each component vector and + * component tensor depend on that choice. + * + * A scalar is a physical quantity that has magnitude but no direction. + * + * Vectors are physical quantities that possess both magnitude and direction + * and whose operations obey the axioms of a vector space. + * + * Tensors can be used to describe more general physical quantities. + * For example, the Cauchy stress tensor possess magnitude, direction, + * and orientation qualities. + */ +enum class quantity_character { scalar, vector, tensor }; + +template +concept common_type_with_ = // exposition only + std::same_as, std::common_type_t> && + std::constructible_from, T> && std::constructible_from, U>; + +template +concept scalable_number_ = // exposition only + std::regular_invocable, T, U> && std::regular_invocable, T, U>; + +template +concept castable_number_ = // exposition only + common_type_with_ && scalable_number_>; + +// TODO Fix it according to quantity_cast implementation +template +concept scalable_ = // exposition only + castable_number_ || (requires { typename T::value_type; } && castable_number_ && + scalable_number_>); + +template +concept Representation = (is_scalar || is_vector || is_tensor)&&std::regular && scalable_; + +template +concept RepresentationOf = Representation && ((Ch == quantity_character::scalar && is_scalar) || + (Ch == quantity_character::vector && is_vector) || + (Ch == quantity_character::tensor && is_tensor)); + +} // namespace mp_units diff --git a/src/core/include/mp_units/bits/unit_concepts.h b/src/core/include/mp_units/bits/unit_concepts.h new file mode 100644 index 00000000..165582e7 --- /dev/null +++ b/src/core/include/mp_units/bits/unit_concepts.h @@ -0,0 +1,159 @@ +// 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 +#include +#include + +namespace mp_units { + +namespace detail { + +template +inline constexpr bool is_unit = false; + +} // namespace detail + +/** + * @brief A concept matching all unit types in the library + * + * Satisfied by all unit types provided by the library. + */ +template +concept Unit = detail::is_unit; + +template +struct scaled_unit; + +template +struct named_unit; + +namespace detail { + +template +void to_base_specialization_of_named_unit(const volatile named_unit*); + +template +inline constexpr bool is_specialization_of_named_unit = false; + +template +inline constexpr bool is_specialization_of_named_unit> = true; + +} // namespace detail + +/** + * @brief A concept matching all units with special names + * + * Satisfied by all unit types derived from the specialization of `named_unit`. + */ +template +concept NamedUnit = Unit && requires(T* t) { detail::to_base_specialization_of_named_unit(t); } && + (!detail::is_specialization_of_named_unit); + +/** + * @brief Prevents assignment of a prefix to specific units + * + * By default all named units allow assigning a prefix for them. There are some notable exceptions like + * `hour` or `degree_Celsius`. For those a partial specialization with the value `false` should be + * provided. + */ +template +inline constexpr bool unit_can_be_prefixed = true; + +/** + * @brief A concept to be used to define prefixes for a unit + */ +template +concept PrefixableUnit = NamedUnit && unit_can_be_prefixed; + +namespace detail { + +template +inline constexpr bool is_power_of_unit = + requires { requires is_specialization_of_power && Unit; }; + +template +inline constexpr bool is_per_of_units = false; + +template +inline constexpr bool is_per_of_units> = (... && (Unit || is_power_of_unit)); + +} // namespace detail + +template +concept DerivedUnitExpr = Unit || detail::is_power_of_unit || detail::is_per_of_units; + +template +struct derived_unit; + +namespace detail { + +template +void is_unit_impl(const scaled_unit*); + +template +void is_unit_impl(const named_unit*); + +template +void is_unit_impl(const derived_unit*); + +template + requires requires(T* t) { is_unit_impl(t); } +inline constexpr bool is_unit = true; + +template +[[nodiscard]] consteval bool has_associated_quantity(U); + +template +[[nodiscard]] consteval bool has_associated_quantity(power) +{ + return has_associated_quantity(U{}); +} + +template +[[nodiscard]] consteval bool has_associated_quantity(type_list) +{ + return (... && has_associated_quantity(Us{})); +} + +template +[[nodiscard]] consteval bool has_associated_quantity(U) +{ + if constexpr (requires { U::reference_unit; }) + return has_associated_quantity(U::reference_unit); + else if constexpr (requires { typename U::_num_; }) + return has_associated_quantity(typename U::_num_{}) && has_associated_quantity(typename U::_den_{}); + else + return requires { U::base_quantity; }; +} + +} // namespace detail + +/** + * @brief A concept matching all units that can be used as quantity references + */ +template +concept AssociatedUnit = Unit && detail::has_associated_quantity(U{}); + +} // namespace mp_units diff --git a/src/core/include/mp_units/concepts.h b/src/core/include/mp_units/concepts.h new file mode 100644 index 00000000..a9a96f00 --- /dev/null +++ b/src/core/include/mp_units/concepts.h @@ -0,0 +1,31 @@ +// 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 +#include +#include +#include +#include +#include +#include diff --git a/src/core/include/mp_units/dimension.h b/src/core/include/mp_units/dimension.h index 3ec8bfd1..231c995b 100644 --- a/src/core/include/mp_units/dimension.h +++ b/src/core/include/mp_units/dimension.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -63,55 +64,14 @@ struct base_dimension { namespace detail { -template -void to_base_base_dimension(const volatile base_dimension*); - -template -inline constexpr bool is_specialization_of_base_dimension = false; - -template -inline constexpr bool is_specialization_of_base_dimension> = true; - -} // namespace detail - -/** - * @brief A concept matching all named base dimensions in the library. - * - * Satisfied by all dimension types derived from a specialization of `base_dimension`. - */ -template -concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); } && - (!detail::is_specialization_of_base_dimension); - -namespace detail { - template struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {}; template using type_list_of_base_dimension_less = expr_less; -template -inline constexpr bool is_dimension_one = false; - -template -inline constexpr bool is_power_of_dim = requires { - requires is_specialization_of_power && (BaseDimension || is_dimension_one); -}; - -template -inline constexpr bool is_per_of_dims = false; - -template -inline constexpr bool is_per_of_dims> = - (... && (BaseDimension || is_dimension_one || is_power_of_dim)); - } // namespace detail -template -concept DerivedDimensionExpr = - BaseDimension || detail::is_dimension_one || detail::is_power_of_dim || detail::is_per_of_dims; - /** * @brief A dimension of a derived quantity * @@ -154,8 +114,8 @@ concept DerivedDimensionExpr = * @note User should not instantiate this type! It is not exported from the C++ module. The library will * instantiate this type automatically based on the dimensional arithmetic equation provided by the user. */ -template -struct derived_dimension : detail::expr_fractions, Ds...> {}; +template +struct derived_dimension : detail::expr_fractions, Expr...> {}; /** * @brief Dimension one @@ -172,33 +132,8 @@ namespace detail { template<> inline constexpr bool is_dimension_one = true; -template -void to_base_specialization_of_derived_dimension(const volatile derived_dimension*); - -template -inline constexpr bool is_derived_from_specialization_of_derived_dimension = - requires(T * t) { to_base_specialization_of_derived_dimension(t); }; - } // namespace detail -/** - * @brief A concept matching all derived dimensions in the library. - * - * Satisfied by all dimension types either being a specialization of `derived_dimension` - * or derived from it (inheritance needed to properly handle `dimension_one`). - */ -template -concept DerivedDimension = detail::is_derived_from_specialization_of_derived_dimension; - -/** - * @brief A concept matching all dimensions in the library. - * - * Satisfied by all dimension types for which either `BaseDimension` or `DerivedDimension` is `true`. - */ -template -concept Dimension = BaseDimension || DerivedDimension; - - // Operators template diff --git a/src/core/include/mp_units/quantity.h b/src/core/include/mp_units/quantity.h index fb3c0274..d186f186 100644 --- a/src/core/include/mp_units/quantity.h +++ b/src/core/include/mp_units/quantity.h @@ -23,8 +23,13 @@ #pragma once +#include #include #include +#include +#include +#include +#include #include #include #include @@ -40,32 +45,31 @@ namespace mp_units { namespace detail { template -concept quantity_one = quantity_of; +concept QuantityOne = QuantityOf; -template +template using quantity_like_type = quantity::reference, typename quantity_like_traits::rep>; -} // namespace detail - template -concept rep_safe_constructible_from_ = // exposition only +concept RepSafeConstructibleFrom = // exposition only std::constructible_from && (treat_as_floating_point || !treat_as_floating_point); // QFrom ratio is an exact multiple of QTo template -concept harmonic_ = // exposition only +concept Harmonic = // exposition only Quantity && Quantity && - is_integral(detail::get_canonical_unit(QFrom::unit).mag / detail::get_canonical_unit(QTo::unit).mag); + is_integral(get_canonical_unit(QFrom::unit).mag / get_canonical_unit(QTo::unit).mag); template -concept quantity_convertible_to_ = // exposition only +concept QuantityConvertibleTo = // exposition only Quantity && Quantity && requires(QFrom q) { quantity_cast(q); } && (treat_as_floating_point || - (!treat_as_floating_point && harmonic_)); + (!treat_as_floating_point && Harmonic)); template -concept invoke_result_of_ = - std::regular_invocable && RepresentationOf, Ch>; +concept InvokeResultOf = std::regular_invocable && RepresentationOf, Ch>; + +} // namespace detail /** * @brief A quantity @@ -76,16 +80,16 @@ concept invoke_result_of_ = * @tparam R a reference of the quantity providing all information about quantity properties * @tparam Rep a type to be used to represent values of a quantity */ -template Rep = double> +template Rep = double> class quantity { public: Rep number_; // needs to be public for a structural type // member types and values static constexpr Reference auto reference = R; - static constexpr QuantitySpec auto quantity_spec = reference.quantity_spec; - static constexpr Dimension auto dimension = reference.dimension; - static constexpr Unit auto unit = reference.unit; + static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference); + static constexpr Dimension auto dimension = quantity_spec.dimension; + static constexpr Unit auto unit = get_unit(reference); using rep = Rep; // static member functions @@ -119,18 +123,18 @@ public: quantity(quantity&&) = default; template - requires rep_safe_constructible_from_ - constexpr explicit(!detail::quantity_one) quantity(Value&& v) : number_(std::forward(v)) + requires detail::RepSafeConstructibleFrom + constexpr explicit(!detail::QuantityOne) quantity(Value&& v) : number_(std::forward(v)) { } - template Q> + template Q> constexpr explicit(false) quantity(const Q& q) : number_(quantity_cast(q).number()) { } - template - requires quantity_convertible_to_, quantity> + template + requires detail::QuantityConvertibleTo, quantity> constexpr explicit quantity(const Q& q) : quantity(detail::quantity_like_type(quantity_like_traits::number(q))) { } @@ -151,7 +155,7 @@ public: } template - requires quantity_convertible_to_{}, Rep>> + requires detail::QuantityConvertibleTo{}, Rep>> [[nodiscard]] constexpr quantity<::mp_units::reference{}, Rep> operator[](U) const { return quantity<::mp_units::reference{}, Rep>{*this}; @@ -252,7 +256,7 @@ public: return *this; } - template + template constexpr quantity& operator*=(const Q& rhs) requires requires(rep a, const typename Q::rep b) { { @@ -277,7 +281,7 @@ public: return *this; } - template + template constexpr quantity& operator/=(const Q& rhs) requires requires(rep a, const typename Q::rep b) { { @@ -303,7 +307,7 @@ public: return *this; } - template + template constexpr quantity& operator%=(const Q& rhs) requires(!treat_as_floating_point) && (!treat_as_floating_point) && requires(rep a, const typename Q::rep b) { @@ -336,7 +340,7 @@ public: requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::mp_units::one; - requires invoke_result_of_, rep, Value>; + requires detail::InvokeResultOf, rep, Value>; } { return ::mp_units::quantity(lhs.number() + rhs); @@ -347,7 +351,7 @@ public: requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::mp_units::one; - requires invoke_result_of_, Value, rep>; + requires detail::InvokeResultOf, Value, rep>; } { return ::mp_units::quantity(lhs + rhs.number()); @@ -358,7 +362,7 @@ public: requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::mp_units::one; - requires invoke_result_of_, rep, Value>; + requires detail::InvokeResultOf, rep, Value>; } { return ::mp_units::quantity(lhs.number() - rhs); @@ -369,14 +373,14 @@ public: requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::mp_units::one; - requires invoke_result_of_, Value, rep>; + requires detail::InvokeResultOf, Value, rep>; } { return ::mp_units::quantity(lhs - rhs.number()); } template - requires invoke_result_of_, rep, const Value&> + requires detail::InvokeResultOf, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v) { using ret = quantity, rep, Value>>; @@ -384,7 +388,7 @@ public: } template - requires invoke_result_of_, const Value&, rep> + requires detail::InvokeResultOf, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q) { using ret = quantity, Value, rep>>; @@ -392,7 +396,7 @@ public: } template - requires(!Quantity) && invoke_result_of_, rep, const Value&> + requires(!Quantity) && detail::InvokeResultOf, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v) { gsl_ExpectsAudit(v != quantity_values::zero()); @@ -401,7 +405,7 @@ public: } template - requires(!Quantity) && invoke_result_of_, const Value&, rep> + requires(!Quantity) && detail::InvokeResultOf, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) { return (dimensionless[::mp_units::one] / reference)(v / q.number()); @@ -409,7 +413,7 @@ public: template requires(!Quantity) && (!treat_as_floating_point) && (!treat_as_floating_point) && - invoke_result_of_, rep, const Value&> + detail::InvokeResultOf, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v) { gsl_ExpectsAudit(v != quantity_values::zero()); @@ -418,7 +422,7 @@ public: } [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) - requires(!treat_as_floating_point) && invoke_result_of_, rep, rep> + requires(!treat_as_floating_point) && detail::InvokeResultOf, rep, rep> { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); using ret = quantity, rep, rep>>; @@ -450,14 +454,14 @@ template Rep> explicit(false) quantity(Rep&&) -> quantity; #endif -template +template explicit quantity(Q) -> quantity::reference, typename quantity_like_traits::rep>; // non-member binary operators template requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) && - invoke_result_of_, - typename Q1::rep, typename Q2::rep> + detail::InvokeResultOf, + typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs) { constexpr auto ref = common_reference(Q1::reference, Q2::reference); @@ -467,8 +471,8 @@ template template requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) && - invoke_result_of_, - typename Q1::rep, typename Q2::rep> + detail::InvokeResultOf, + typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs) { constexpr auto ref = common_reference(Q1::reference, Q2::reference); @@ -477,16 +481,16 @@ template } template - requires invoke_result_of_<(Q1::quantity_spec * Q2::quantity_spec).character, std::multiplies<>, typename Q1::rep, - typename Q2::rep> + requires detail::InvokeResultOf<(Q1::quantity_spec * Q2::quantity_spec).character, std::multiplies<>, + typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator*(const Q1& lhs, const Q2& rhs) { return (Q1::reference * Q2::reference)(lhs.number() * rhs.number()); } template - requires invoke_result_of_<(Q1::quantity_spec / Q2::quantity_spec).character, std::divides<>, typename Q1::rep, - typename Q2::rep> + requires detail::InvokeResultOf<(Q1::quantity_spec / Q2::quantity_spec).character, std::divides<>, typename Q1::rep, + typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator/(const Q1& lhs, const Q2& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); @@ -495,8 +499,8 @@ template template requires(!treat_as_floating_point) && (!treat_as_floating_point) && - (interconvertible(Q1::reference, Q2::reference) || quantity_of) && - invoke_result_of_, typename Q1::rep, typename Q2::rep> + (interconvertible(Q1::reference, Q2::reference) || QuantityOf) && + detail::InvokeResultOf, typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); diff --git a/src/core/include/mp_units/quantity_point.h b/src/core/include/mp_units/quantity_point.h index 73a91ea2..080199bc 100644 --- a/src/core/include/mp_units/quantity_point.h +++ b/src/core/include/mp_units/quantity_point.h @@ -23,15 +23,21 @@ #pragma once +#include #include #include #include namespace mp_units { +template +struct absolute_point_origin { + static constexpr QuantitySpec auto quantity_spec = Q; +}; + namespace detail { -[[nodiscard]] consteval point_origin auto get_absolute_point_origin(point_origin auto po) +[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PointOrigin auto po) { if constexpr (requires { po.absolute_point_origin; }) return po.absolute_point_origin; @@ -39,7 +45,7 @@ namespace detail { return po; } -template +template using quantity_point_like_type = quantity_point::reference, quantity_point_like_traits::point_origin, typename quantity_point_like_traits::rep>; @@ -55,17 +61,17 @@ using quantity_point_like_type = * @tparam PO a type that represents the origin point from which the quantity point is measured from * @tparam Rep a type to be used to represent values of a quantity point */ -template auto PO = absolute_point_origin{}, - RepresentationOf Rep = double> +template auto PO = absolute_point_origin{}, + RepresentationOf Rep = double> class quantity_point { public: // member types and values static constexpr Reference auto reference = R; - static constexpr QuantitySpec auto quantity_spec = reference.quantity_spec; - static constexpr Dimension auto dimension = reference.dimension; - static constexpr Unit auto unit = reference.unit; - static constexpr point_origin auto absolute_point_origin = detail::get_absolute_point_origin(PO); - static constexpr point_origin auto point_origin = PO; + static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference); + static constexpr Dimension auto dimension = quantity_spec.dimension; + static constexpr Unit auto unit = get_unit(reference); + static constexpr PointOrigin auto absolute_point_origin = detail::get_absolute_point_origin(PO); + static constexpr PointOrigin auto point_origin = PO; using rep = Rep; using quantity_type = quantity; @@ -95,13 +101,13 @@ public: { } - template QP2> + template QP2> requires std::convertible_to constexpr explicit(false) quantity_point(const QP2& qp) : q_(qp.relative()) { } - template + template requires std::same_as::point_origin)>, std::remove_const_t> && std::convertible_to::quantity_type, quantity_type> @@ -195,21 +201,21 @@ public: return quantity_point(q); } - template QP> + template QP> [[nodiscard]] friend constexpr Quantity auto operator-(const quantity_point& lhs, const QP& rhs) requires requires(quantity_type q) { q - rhs.absolute(); } { return lhs.absolute() - rhs.absolute(); } - template QP> + 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 QP> + template QP> requires std::equality_comparable_with [[nodiscard]] friend constexpr bool operator==(const quantity_point& lhs, const QP& rhs) { @@ -224,12 +230,12 @@ explicit quantity_point(Rep) -> quantity_point explicit quantity_point(Q) -> quantity_point{}, typename Q::rep>; -template +template explicit quantity_point(Q) -> quantity_point::reference, absolute_point_origin::quantity_spec>{}, typename quantity_like_traits::rep>; -template +template explicit quantity_point(QP) -> quantity_point::reference, quantity_point_like_traits::point_origin, typename quantity_point_like_traits::rep>; diff --git a/src/core/include/mp_units/reference.h b/src/core/include/mp_units/reference.h index c7e3dadd..b4b08721 100644 --- a/src/core/include/mp_units/reference.h +++ b/src/core/include/mp_units/reference.h @@ -23,10 +23,65 @@ #pragma once #include -#include +#include +#include +#include namespace mp_units { +namespace detail { + +template +[[nodiscard]] consteval auto get_associated_quantity(U); + +template +[[nodiscard]] consteval auto get_associated_quantity(power) +{ + return get_associated_quantity(U{}); +} + +template +[[nodiscard]] consteval auto get_associated_quantity(type_list) +{ + return (dimensionless * ... * get_associated_quantity(Us{})); +} + +template +[[nodiscard]] consteval auto get_associated_quantity(U) +{ + if constexpr (requires { U::reference_unit; }) + return get_associated_quantity(U::reference_unit); + else if constexpr (requires { typename U::_num_; }) + return get_associated_quantity(typename U::_num_{}) / get_associated_quantity(typename U::_den_{}); + else if constexpr (requires { U::base_quantity; }) + return U::base_quantity; +} + +} // namespace detail + +[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u) +{ + return detail::get_associated_quantity(u); +} + +template +[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(reference) +{ + return Q; +} + +[[nodiscard]] consteval Unit auto get_unit(AssociatedUnit auto u) { return u; } + +template +[[nodiscard]] consteval Unit auto get_unit(reference) +{ + return U; +} + + +template Rep> +class quantity; + /** * @brief Quantity reference type * @@ -39,7 +94,7 @@ namespace mp_units { * * @code{.cpp} * Reference auto kmph = isq::speed[km / h]; - * quantity_of auto speed = 90 * kmph; + * QuantityOf auto speed = 90 * kmph; * @endcode * * The following syntaxes are not allowed: @@ -47,10 +102,6 @@ namespace mp_units { */ template struct reference { - static constexpr QuantitySpec auto quantity_spec = Q; - static constexpr Dimension auto dimension = Q.dimension; - static constexpr Unit auto unit = U; - template Rep> // TODO can we somehow return an explicit quantity type here? [[nodiscard]] constexpr std::same_as> auto operator()(Rep&& value) const @@ -59,54 +110,103 @@ struct reference { } }; -// Reference +template +[[nodiscard]] consteval bool operator==(reference, reference) +{ + return Q1 == Q2 && U1 == U2; +} -template -[[nodiscard]] consteval reference operator*(R1, R2) +template +[[nodiscard]] consteval bool operator==(reference, U2 u2) +{ + return Q1 == get_quantity_spec(u2) && U1 == u2; +} + +template +[[nodiscard]] consteval reference operator*(reference, reference) { return {}; } -template -[[nodiscard]] consteval reference operator/(R1, R2) +template +[[nodiscard]] consteval reference operator*(reference, U2) { return {}; } -// TODO remove when all code is refactored to a new syntax -template +template +[[nodiscard]] consteval reference operator*(U1, reference) +{ + return {}; +} + +template +[[nodiscard]] consteval reference operator/(reference, reference) +{ + return {}; +} + +template +[[nodiscard]] consteval reference operator/(reference, U2) +{ + return {}; +} + +template +[[nodiscard]] consteval reference operator/(U1, reference) +{ + return {}; +} + +template Rep> [[nodiscard]] constexpr quantity operator*(const Rep& lhs, R) { return quantity(lhs); } -// TODO remove when all code is refactored to a new syntax void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete; -template -[[nodiscard]] consteval bool operator==(R1, R2) +template +[[nodiscard]] consteval bool interconvertible(reference, reference) { - return R1::quantity_spec == R2::quantity_spec && R1::unit == R2::unit; + return interconvertible(Q1, Q2) && interconvertible(U1, U2); } -template -[[nodiscard]] consteval bool interconvertible(R1, R2) +template +[[nodiscard]] consteval bool interconvertible(reference, U2 u2) { - return interconvertible(R1::quantity_spec, R2::quantity_spec) && interconvertible(R1::unit, R2::unit); + return interconvertible(Q1, get_quantity_spec(u2)) && interconvertible(U1, u2); +} + +template +[[nodiscard]] consteval bool interconvertible(U1 u1, reference r2) +{ + return interconvertible(r2, u1); +} + +[[nodiscard]] consteval auto common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2, + AssociatedUnit auto... rest) + requires requires { + { + common_unit(u1, u2, rest...) + } -> AssociatedUnit; + } +{ + return common_unit(u1, u2, rest...); } [[nodiscard]] consteval auto common_reference(Reference auto r1, Reference auto r2, Reference auto... rest) requires requires { { - common_quantity_spec(r1.quantity_spec, r2.quantity_spec, rest.quantity_spec...) + common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...) } -> QuantitySpec; { - common_unit(r1.unit, r2.unit, rest.unit...) + common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...) } -> Unit; } { - return reference{}; + return reference{}; } } // namespace mp_units diff --git a/src/core/include/mp_units/system_reference.h b/src/core/include/mp_units/system_reference.h index 78b83ff7..354a5797 100644 --- a/src/core/include/mp_units/system_reference.h +++ b/src/core/include/mp_units/system_reference.h @@ -56,7 +56,7 @@ namespace mp_units { * @tparam CoU coherent unit for a quantity in this system */ template - requires(!detail::associated_unit>) || (CoU == one) + requires(!AssociatedUnit>) || (CoU == one) struct system_reference { static constexpr auto quantity_spec = Q; static constexpr auto coherent_unit = CoU; diff --git a/src/core/include/mp_units/unit.h b/src/core/include/mp_units/unit.h index 39726c17..651138d8 100644 --- a/src/core/include/mp_units/unit.h +++ b/src/core/include/mp_units/unit.h @@ -28,84 +28,17 @@ #include #include #include +#include #include #include #include +#include #include #include #include namespace mp_units { -#ifdef __cpp_explicit_this_parameter -template -#else -template -#endif -struct quantity_spec; - -namespace detail { - -#ifdef __cpp_explicit_this_parameter -template -void to_base_specialization_of_quantity_spec(const volatile quantity_spec*); -#else -template -void to_base_specialization_of_quantity_spec(const volatile quantity_spec*); -#endif - -#ifdef __cpp_explicit_this_parameter -template -template -void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec*); -#else -template -void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec*); -#endif - -template -inline constexpr bool is_specialization_of_quantity_spec = false; - -#ifdef __cpp_explicit_this_parameter -template -inline constexpr bool is_specialization_of_quantity_spec> = true; -#else -template -inline constexpr bool is_specialization_of_quantity_spec> = true; -#endif - -} // namespace detail - -/** - * @brief Concept matching quantity specification types - * - * Satisfied by all types that derive from `quantity_spec`. - */ -template -concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_quantity_spec(t); } && - (!detail::is_specialization_of_quantity_spec); - -template -concept BaseQuantitySpec = - NamedQuantitySpec && requires(T* t) { detail::to_base_specialization_of_base_quantity_spec(t); }; - - -namespace detail { - -template -inline constexpr bool is_unit = false; - -} // namespace detail - -/** - * @brief A concept matching all unit types in the library - * - * Satisfied by all unit types provided by the library. - */ -template -concept Unit = detail::is_unit; - - /** * @brief Unit being a scaled version of another unit * @@ -213,29 +146,6 @@ struct named_unit : std::remove_const_t { static constexpr auto symbol = Symbol; ///< Unique unit identifier }; -namespace detail { - -template -void to_base_specialization_of_named_unit(const volatile named_unit*); - -template -inline constexpr bool is_specialization_of_named_unit = false; - -template -inline constexpr bool is_specialization_of_named_unit> = true; - -} // namespace detail - -/** - * @brief A concept matching all units with special names - * - * Satisfied by all unit types derived from the specialization of `named_unit`. - */ -template -concept NamedUnit = Unit && requires(T* t) { detail::to_base_specialization_of_named_unit(t); } && - (!detail::is_specialization_of_named_unit); - - /** * @brief A unit of a physical constant * @@ -278,28 +188,10 @@ void to_base_specialization_of_constant_unit(const volatile constant_unit inline constexpr bool is_derived_from_specialization_of_constant_unit = - requires(T * t) { to_base_specialization_of_constant_unit(t); }; + requires(T* t) { to_base_specialization_of_constant_unit(t); }; } // namespace detail -/** - * @brief Prevents assignment of a prefix to specific units - * - * By default all named units allow assigning a prefix for them. There are some notable exceptions like - * `hour` or `degree_Celsius`. For those a partial specialization with the value `false` should be - * provided. - */ -template -inline constexpr bool unit_can_be_prefixed = true; - - -/** - * @brief A concept to be used to define prefixes for a unit - */ -template -concept PrefixableUnit = NamedUnit && unit_can_be_prefixed; - - /** * @brief A prefixed unit * @@ -328,23 +220,6 @@ struct prefixed_unit : std::remove_const_t { static constexpr auto symbol = Symbol + U.symbol; }; -namespace detail { - -template -inline constexpr bool is_power_of_unit = - requires { requires is_specialization_of_power && Unit; }; - -template -inline constexpr bool is_per_of_units = false; - -template -inline constexpr bool is_per_of_units> = (... && (Unit || is_power_of_unit)); - -} // namespace detail - -template -concept DerivedUnitExpr = Unit || detail::is_power_of_unit || detail::is_per_of_units; - /** * @brief Measurement unit for a derived quantity * @@ -390,8 +265,8 @@ concept DerivedUnitExpr = Unit || detail::is_power_of_unit || detail::is_p * @note User should not instantiate this type! It is not exported from the C++ module. The library will * instantiate this type automatically based on the unit arithmetic equation provided by the user. */ -template -struct derived_unit : detail::expr_fractions, Us...> {}; +template +struct derived_unit : detail::expr_fractions, Expr...> {}; /** * @brief Unit one @@ -404,19 +279,6 @@ inline constexpr struct one : derived_unit<> {} one; namespace detail { -template -void is_unit_impl(const volatile scaled_unit*); - -template -void is_unit_impl(const volatile named_unit*); - -template -void is_unit_impl(const volatile derived_unit*); - -template - requires requires(T* t) { is_unit_impl(t); } -inline constexpr bool is_unit = true; - /** * @brief A canonical representation of a unit * @@ -452,8 +314,8 @@ template template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const power&); -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit&); +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit&); template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit&) @@ -511,16 +373,39 @@ template return canonical_unit{mag, u}; } -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit&) +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit&) { - auto num = get_canonical_unit_impl(typename derived_unit::_num_{}); - auto den = get_canonical_unit_impl(typename derived_unit::_den_{}); + auto num = get_canonical_unit_impl(typename derived_unit::_num_{}); + auto den = get_canonical_unit_impl(typename derived_unit::_den_{}); return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit}; } [[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return get_canonical_unit_impl(u, u); } +template + requires requires { U::base_quantity.dimension; } +using to_base_dimension = std::remove_const_t; + +template + requires requires { U::base_quantity.dimension; } +[[nodiscard]] consteval Dimension auto get_dimension_for_impl(U) +{ + return U::base_quantity.dimension; +} + +template + requires expr_projectable, to_base_dimension> +[[nodiscard]] consteval Dimension auto get_dimension_for_impl(const derived_unit& u) +{ + return expr_map(u); +} + +[[nodiscard]] consteval Dimension auto get_dimension_for(AssociatedUnit auto u) +{ + return get_dimension_for_impl(get_canonical_unit(u).reference_unit); +} + template [[nodiscard]] consteval bool less(Lhs, Rhs) { @@ -640,12 +525,13 @@ template return (... && same_canonical_reference_unit(Us1{}, Us2{})); } -template -[[nodiscard]] consteval bool same_canonical_reference_unit(const derived_unit&, const derived_unit&) +template +[[nodiscard]] consteval bool same_canonical_reference_unit(const derived_unit&, const derived_unit&) { - return same_canonical_reference_unit(typename derived_unit::_num_{}, - typename derived_unit::_num_{}) && - same_canonical_reference_unit(typename derived_unit::_den_{}, typename derived_unit::_den_{}); + return same_canonical_reference_unit(typename derived_unit::_num_{}, + typename derived_unit::_num_{}) && + same_canonical_reference_unit(typename derived_unit::_den_{}, + typename derived_unit::_den_{}); } } // namespace detail @@ -898,11 +784,11 @@ constexpr Out unit_symbol_impl(Out out, const type_list& nums, const ty } } -template Out, typename... Us> -constexpr Out unit_symbol_impl(Out out, const derived_unit&, unit_symbol_formatting fmt, bool negative_power) +template Out, typename... Expr> +constexpr Out unit_symbol_impl(Out out, const derived_unit&, unit_symbol_formatting fmt, bool negative_power) { gsl_Expects(negative_power == false); - return unit_symbol_impl(out, typename derived_unit::_num_{}, typename derived_unit::_den_{}, + return unit_symbol_impl(out, typename derived_unit::_num_{}, typename derived_unit::_den_{}, fmt); } diff --git a/src/utility/include/mp_units/chrono.h b/src/utility/include/mp_units/chrono.h index 25f621ea..9534211a 100644 --- a/src/utility/include/mp_units/chrono.h +++ b/src/utility/include/mp_units/chrono.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include @@ -80,7 +81,7 @@ struct quantity_point_like_traits Q> +template Q> [[nodiscard]] constexpr auto to_chrono_duration(const Q& q) { constexpr auto canonical = detail::get_canonical_unit(Q::unit); @@ -88,7 +89,7 @@ template Q> return std::chrono::duration>{q.number()}; } -template QP> +template QP> requires is_specialization_of, chrono_point_origin> [[nodiscard]] constexpr auto to_chrono_time_point(const QP& qp) { diff --git a/src/utility/include/mp_units/math.h b/src/utility/include/mp_units/math.h index 8092d851..32632d75 100644 --- a/src/utility/include/mp_units/math.h +++ b/src/utility/include/mp_units/math.h @@ -109,7 +109,7 @@ template * @param q Quantity being the base of the operation * @return Quantity The value of the same quantity type */ -template Q> +template Q> [[nodiscard]] inline Q exp(const Q& q) requires requires { exp(q.number()); } || requires { std::exp(q.number()); } { @@ -153,12 +153,12 @@ template * @return Quantity The rounded quantity with unit type To */ template -[[nodiscard]] constexpr quantity{}, Rep> floor(const quantity& q) noexcept +[[nodiscard]] constexpr quantity{}, Rep> floor(const quantity& q) noexcept requires((!treat_as_floating_point) || requires { floor(q.number()); } || requires { std::floor(q.number()); }) && - (To == R.unit || requires { + (To == get_unit(R) || requires { ::mp_units::quantity_cast(q); - quantity{}, Rep>::one(); + quantity{}, Rep>::one(); }) { const auto handle_signed_results = [&](const T& res) { @@ -169,14 +169,14 @@ template }; if constexpr (treat_as_floating_point) { using std::floor; - if constexpr (To == R.unit) { - return quantity{}, Rep>(floor(q.number())); + if constexpr (To == get_unit(R)) { + return quantity{}, Rep>(floor(q.number())); } else { return handle_signed_results( - quantity{}, Rep>(floor(quantity_cast(q).number()))); + quantity{}, Rep>(floor(quantity_cast(q).number()))); } } else { - if constexpr (To == R.unit) { + if constexpr (To == get_unit(R)) { return quantity_cast(q); } else { return handle_signed_results(quantity_cast(q)); @@ -191,11 +191,11 @@ template * @return Quantity The rounded quantity with unit type To */ template -[[nodiscard]] constexpr quantity{}, Rep> ceil(const quantity& q) noexcept +[[nodiscard]] constexpr quantity{}, Rep> ceil(const quantity& q) noexcept requires((!treat_as_floating_point) || requires { ceil(q.number()); } || requires { std::ceil(q.number()); }) && - (To == R.unit || requires { + (To == get_unit(R) || requires { ::mp_units::quantity_cast(q); - quantity{}, Rep>::one(); + quantity{}, Rep>::one(); }) { const auto handle_signed_results = [&](const T& res) { @@ -206,14 +206,14 @@ template }; if constexpr (treat_as_floating_point) { using std::ceil; - if constexpr (To == R.unit) { - return quantity{}, Rep>(ceil(q.number())); + if constexpr (To == get_unit(R)) { + return quantity{}, Rep>(ceil(q.number())); } else { return handle_signed_results( - quantity{}, Rep>(ceil(quantity_cast(q).number()))); + quantity{}, Rep>(ceil(quantity_cast(q).number()))); } } else { - if constexpr (To == R.unit) { + if constexpr (To == get_unit(R)) { return quantity_cast(q); } else { return handle_signed_results(quantity_cast(q)); @@ -230,18 +230,18 @@ template * @return Quantity The rounded quantity with unit type To */ template -[[nodiscard]] constexpr quantity{}, Rep> round(const quantity& q) noexcept +[[nodiscard]] constexpr quantity{}, Rep> round(const quantity& q) noexcept requires((!treat_as_floating_point) || requires { round(q.number()); } || requires { std::round(q.number()); }) && - (To == R.unit || requires { + (To == get_unit(R) || requires { ::mp_units::floor(q); - quantity{}, Rep>::one(); + quantity{}, Rep>::one(); }) { - if constexpr (To == R.unit) { + if constexpr (To == get_unit(R)) { if constexpr (treat_as_floating_point) { using std::round; - return quantity{}, Rep>(round(q.number())); + return quantity{}, Rep>(round(q.number())); } else { return quantity_cast(q); } @@ -268,7 +268,7 @@ template * without undue overflow or underflow at intermediate stages of the computation */ template -[[nodiscard]] inline quantity_of auto hypot(const Q1& x, +[[nodiscard]] inline QuantityOf auto hypot(const Q1& x, const Q2& y) noexcept requires requires { common_reference(Q1::reference, Q2::reference); } && ( @@ -284,7 +284,7 @@ template * without undue overflow or underflow at intermediate stages of the computation */ template -[[nodiscard]] inline quantity_of auto hypot( +[[nodiscard]] inline QuantityOf auto hypot( const Q1& x, const Q2& y, const Q3& z) noexcept requires requires { common_reference(Q1::reference, Q2::reference, Q3::reference); } && ( @@ -299,54 +299,54 @@ template namespace isq { -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto sin(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto sin(const Q& q) noexcept requires requires { sin(q.number()); } || requires { std::sin(q.number()); } { using std::sin; return quantity{sin(q[si::radian].number())}; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto cos(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto cos(const Q& q) noexcept requires requires { cos(q.number()); } || requires { std::cos(q.number()); } { using std::cos; return quantity{cos(q[si::radian].number())}; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto tan(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto tan(const Q& q) noexcept requires requires { tan(q.number()); } || requires { std::tan(q.number()); } { using std::tan; return quantity{tan(q[si::radian].number())}; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto asin(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto asin(const Q& q) noexcept requires requires { asin(q.number()); } || requires { std::asin(q.number()); } { using std::asin; return asin(quantity_cast(q).number()) * angular_measure[si::radian]; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto acos(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto acos(const Q& q) noexcept requires requires { acos(q.number()); } || requires { std::acos(q.number()); } { using std::acos; return acos(quantity_cast(q).number()) * angular_measure[si::radian]; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto atan(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto atan(const Q& q) noexcept requires requires { atan(q.number()); } || requires { std::atan(q.number()); } { using std::atan; @@ -357,55 +357,55 @@ template Q> namespace angular { -// TODO cannot use `weak_quantity_of` as it is not interconvertible with `isq_angle::angular_measure` -template Q> +// TODO cannot use `WeakQuantityOf` as it is not interconvertible with `isq_angle::angular_measure` +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto sin(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto sin(const Q& q) noexcept requires requires { sin(q.number()); } || requires { std::sin(q.number()); } { using std::sin; return quantity{sin(q[radian].number())}; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto cos(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto cos(const Q& q) noexcept requires requires { cos(q.number()); } || requires { std::cos(q.number()); } { using std::cos; return quantity{cos(q[radian].number())}; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto tan(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto tan(const Q& q) noexcept requires requires { tan(q.number()); } || requires { std::tan(q.number()); } { using std::tan; return quantity{tan(q[radian].number())}; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto asin(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto asin(const Q& q) noexcept requires requires { asin(q.number()); } || requires { std::asin(q.number()); } { using std::asin; return asin(quantity_cast(q).number()) * angle[radian]; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto acos(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto acos(const Q& q) noexcept requires requires { acos(q.number()); } || requires { std::acos(q.number()); } { using std::acos; return acos(quantity_cast(q).number()) * angle[radian]; } -template Q> +template Q> requires treat_as_floating_point -[[nodiscard]] inline quantity_of auto atan(const Q& q) noexcept +[[nodiscard]] inline QuantityOf auto atan(const Q& q) noexcept requires requires { atan(q.number()); } || requires { std::atan(q.number()); } { using std::atan; diff --git a/test/unit_test/static/dimension_test.cpp b/test/unit_test/static/dimension_test.cpp index d1a22b79..c6534d05 100644 --- a/test/unit_test/static/dimension_test.cpp +++ b/test/unit_test/static/dimension_test.cpp @@ -60,13 +60,13 @@ inline constexpr auto energy = force * length; // concepts verification static_assert(BaseDimension); static_assert(!BaseDimension>); -static_assert(!DerivedDimension); -static_assert(DerivedDimension>); +static_assert(!detail::DerivedDimension); +static_assert(detail::DerivedDimension>); static_assert(Dimension); static_assert(Dimension>); -static_assert(DerivedDimension); -static_assert(DerivedDimension); // dimension_one +static_assert(detail::DerivedDimension); +static_assert(detail::DerivedDimension); // dimension_one static_assert(BaseDimension); // length // derived dimension expression template syntax verification diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 0e9fd137..9ade6e1a 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -397,7 +397,6 @@ template concept invalid_operations = requires { requires !requires { s < s; }; requires !requires { s / 2; }; - requires !requires { 2 * s; }; requires !requires { s * 2; }; requires !requires { s + 2; }; requires !requires { 2 + s; }; @@ -411,16 +410,9 @@ concept invalid_operations = requires { requires !requires { 2 < s; }; requires !requires { s + time[second]; }; requires !requires { s - time[second]; }; - requires !requires { s* time[second]; }; - requires !requires { s / time[second]; }; - requires !requires { s == time[second]; }; requires !requires { s < time[second]; }; requires !requires { time[second] + s; }; requires !requires { time[second] - s; }; - requires !requires { time[second] * s; }; - requires !requires { time[second] / s; }; - requires !requires { time[second] == s; }; - requires !requires { time[second] < s; }; requires !requires { s + 1 * time[second]; }; requires !requires { s - 1 * time[second]; }; requires !requires { s * 1 * time[second]; };