From e058a47bafc48757fcdd9870fa7e5fd49f937d76 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 9 Jul 2025 20:05:03 +0200 Subject: [PATCH] feat: natural units support is now opt-in --- conanfile.py | 3 + .../getting_started/installation_and_usage.md | 16 +++++ example/CMakeLists.txt | 4 +- src/CMakeLists.txt | 2 + src/core/CMakeLists.txt | 16 ++++- .../mp-units/bits/get_associated_quantity.h | 11 ++-- src/core/include/mp-units/bits/hacks.h | 7 +++ src/core/include/mp-units/compat_macros.h | 14 +++++ src/core/include/mp-units/framework.h | 2 + .../include/mp-units/framework/quantity.h | 12 ++-- .../mp-units/framework/quantity_cast.h | 5 +- .../mp-units/framework/quantity_point.h | 9 +-- .../mp-units/framework/quantity_spec.h | 3 +- .../include/mp-units/framework/reference.h | 30 +++++---- .../mp-units/framework/reference_concepts.h | 7 ++- .../mp-units/framework/system_reference.h | 7 ++- src/core/include/mp-units/framework/unit.h | 9 ++- .../mp-units/framework/unit_concepts.h | 18 ++++-- .../include/mp-units/framework/value_cast.h | 39 ++++++------ src/core/include/mp-units/math.h | 11 +++- src/systems/CMakeLists.txt | 8 ++- src/systems/mp-units-systems.cpp | 2 + test/static/CMakeLists.txt | 5 +- test/static/concepts_test.cpp | 61 +++++++++++-------- test/static/reference_test.cpp | 4 ++ test/static/unit_test.cpp | 12 +++- 26 files changed, 222 insertions(+), 95 deletions(-) diff --git a/conanfile.py b/conanfile.py index f44bc3fe..8eee21ac 100644 --- a/conanfile.py +++ b/conanfile.py @@ -61,6 +61,7 @@ class MPUnitsConan(ConanFile): "no_crtp": [True, False], "contracts": ["none", "gsl-lite", "ms-gsl"], "freestanding": [True, False], + "natural_units": [True, False], } default_options = { # "cxx_modules" default set in config_options() @@ -69,6 +70,7 @@ class MPUnitsConan(ConanFile): # "no_crtp" default set in config_options() "contracts": "gsl-lite", "freestanding": False, + "natural_units": True, } implements = ["auto_header_only"] exports = "LICENSE.md" @@ -281,6 +283,7 @@ class MPUnitsConan(ConanFile): tc.cache_variables["MP_UNITS_API_STD_FORMAT"] = opt.std_format tc.cache_variables["MP_UNITS_API_NO_CRTP"] = opt.no_crtp tc.cache_variables["MP_UNITS_API_CONTRACTS"] = str(opt.contracts).upper() + tc.cache_variables["MP_UNITS_API_NATURAL_UNITS"] = opt.natural_units tc.generate() deps = CMakeDeps(self) diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 9128c54c..7ae0bbdb 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -154,6 +154,14 @@ dependencies by other means, some modifications to the library's CMake files mig [conan freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`natural_units`](#natural_units){ #natural_units } + +: [:octicons-tag-24: 2.5.0][conan natural units] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`) + + Enables experimental natural units systems support. + + [conan natural units]: https://github.com/mpusz/mp-units/releases/tag/v2.5.0 + ??? info "CMake options to set when Conan is not being used" ### CMake options @@ -215,6 +223,14 @@ dependencies by other means, some modifications to the library's CMake files mig [cmake freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 + [`MP_UNITS_API_NATURAL_UNITS`](#MP_UNITS_API_NATURAL_UNITS){ #MP_UNITS_API_NATURAL_UNITS } + + : [:octicons-tag-24: 2.5.0][cmake natural units] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`) + + Enables experimental natural units systems support. + + [cmake natural units]: https://github.com/mpusz/mp-units/releases/tag/v2.5.0 + ## Installation and reuse diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index ed36e396..8f30c5f4 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -69,7 +69,9 @@ add_example(si_constants) add_example(spectroscopy_units) add_example(storage_tank) add_example(strong_angular_quantities) -add_example(total_energy) +if(${projectPrefix}API_NATURAL_UNITS) + add_example(total_energy) +endif() add_example(unmanned_aerial_vehicle example_utils) add_subdirectory(glide_computer_lib) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cf54f05f..f1a0d746 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -87,12 +87,14 @@ option(${projectPrefix}API_THROWING_CONSTRAINTS "Enable throwing constraints" option(${projectPrefix}API_FREESTANDING "Builds only freestanding part of the library" OFF) set(${projectPrefix}API_CONTRACTS GSL-LITE CACHE STRING "Enable contract checking") check_cache_var_values(API_CONTRACTS NONE GSL-LITE MS-GSL) +option(${projectPrefix}API_NATURAL_UNITS "Enables natural units support" ON) message(STATUS "${projectPrefix}API_STD_FORMAT: ${${projectPrefix}API_STD_FORMAT}") message(STATUS "${projectPrefix}API_NO_CRTP: ${${projectPrefix}API_NO_CRTP}") message(STATUS "${projectPrefix}API_THROWING_CONSTRAINTS: ${${projectPrefix}API_THROWING_CONSTRAINTS}") message(STATUS "${projectPrefix}API_FREESTANDING: ${${projectPrefix}API_FREESTANDING}") message(STATUS "${projectPrefix}API_CONTRACTS: ${${projectPrefix}API_CONTRACTS}") +message(STATUS "${projectPrefix}API_NATURAL_UNITS: ${${projectPrefix}API_NATURAL_UNITS}") # validate options if(${projectPrefix}API_FREESTANDING AND NOT ${projectPrefix}API_CONTRACTS STREQUAL "NONE") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7a3c63b5..abec497a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -67,7 +67,6 @@ add_mp_units_module( include/mp-units/framework/representation_concepts.h include/mp-units/framework/symbol_text.h include/mp-units/framework/symbolic_expression.h - include/mp-units/framework/system_reference.h include/mp-units/framework/unit.h include/mp-units/framework/unit_concepts.h include/mp-units/framework/unit_magnitude.h @@ -81,6 +80,13 @@ add_mp_units_module( MODULE_INTERFACE_UNIT mp-units-core.cpp ) +if(${${projectPrefix}API_NATURAL_UNITS}) + target_sources( + mp-units-core PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include FILES + include/mp-units/framework/system_reference.h + ) +endif() + if(NOT ${projectPrefix}API_FREESTANDING) target_sources( mp-units-core @@ -171,6 +177,14 @@ if(${projectPrefix}API_THROWING_CONSTRAINTS) ) endif() +# Natural units support +if(${${projectPrefix}API_NATURAL_UNITS}) + target_compile_definitions( + mp-units-core ${${projectPrefix}TARGET_SCOPE} + ${projectPrefix}API_NATURAL_UNITS=$ + ) +endif() + # https://github.com/llvm/llvm-project/issues/131410 if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 20 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 20.2 diff --git a/src/core/include/mp-units/bits/get_associated_quantity.h b/src/core/include/mp-units/bits/get_associated_quantity.h index 5d7674e7..02a7b721 100644 --- a/src/core/include/mp-units/bits/get_associated_quantity.h +++ b/src/core/include/mp-units/bits/get_associated_quantity.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -33,7 +34,7 @@ struct common_unit; namespace detail { -template +template [[nodiscard]] consteval auto get_associated_quantity(U u); template @@ -42,10 +43,10 @@ template return get_common_quantity_spec(get_associated_quantity(Us{})...); } -template +template using to_quantity_spec = decltype(get_associated_quantity(U{})); -template +template [[nodiscard]] consteval auto get_associated_quantity_impl(U u) { if constexpr (requires { U::_quantity_spec_; }) @@ -56,10 +57,10 @@ template return expr_map(u); } -template +template constexpr auto get_associated_quantity_result = get_associated_quantity_impl(U{}); -template +template [[nodiscard]] consteval auto get_associated_quantity(U) { return get_associated_quantity_result; diff --git a/src/core/include/mp-units/bits/hacks.h b/src/core/include/mp-units/bits/hacks.h index d179726b..8ead009e 100644 --- a/src/core/include/mp-units/bits/hacks.h +++ b/src/core/include/mp-units/bits/hacks.h @@ -158,6 +158,13 @@ MP_UNITS_DIAGNOSTIC_POP #endif +#if !defined MP_UNITS_API_NATURAL_UNITS + +#define MP_UNITS_API_NATURAL_UNITS 1 + +#endif + + #if defined(__clang__) && defined(__apple_build_version__) && __apple_build_version__ < 16000026 #define MP_UNITS_XCODE15_HACKS #endif diff --git a/src/core/include/mp-units/compat_macros.h b/src/core/include/mp-units/compat_macros.h index 4add3e12..9f73c655 100644 --- a/src/core/include/mp-units/compat_macros.h +++ b/src/core/include/mp-units/compat_macros.h @@ -40,6 +40,20 @@ #endif +#if MP_UNITS_API_NATURAL_UNITS + +#define MP_UNITS_ASSOCIATED_UNIT AssociatedUnit +#define MP_UNITS_ASSOCIATED_UNIT_T(U) AssociatedUnit +#define MP_UNITS_WEAK_UNIT_OF(...) detail::WeakUnitOf<__VA_ARGS__> + +#else + +#define MP_UNITS_ASSOCIATED_UNIT Unit +#define MP_UNITS_ASSOCIATED_UNIT_T(U) Unit +#define MP_UNITS_WEAK_UNIT_OF(...) UnitOf<__VA_ARGS__> + +#endif + #if MP_UNITS_HOSTED #define MP_UNITS_THROW(expr) throw expr #else diff --git a/src/core/include/mp-units/framework.h b/src/core/include/mp-units/framework.h index 728ddb87..f4802c00 100644 --- a/src/core/include/mp-units/framework.h +++ b/src/core/include/mp-units/framework.h @@ -39,7 +39,9 @@ #include #include #include +#if MP_UNITS_API_NATURAL_UNITS #include +#endif #include #include #include diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 7e53a48e..05ed92a5 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -244,7 +244,7 @@ public: return *this; } - template ToU> + template requires detail::ValuePreservingScaling [[nodiscard]] constexpr QuantityOf auto in(ToU) const { @@ -258,7 +258,7 @@ public: return quantity{*this}; } - template ToRep, detail::WeakUnitOf ToU> + template ToRep, MP_UNITS_WEAK_UNIT_OF(quantity_spec) ToU> requires detail::ValuePreservingConstruction && detail::ValuePreservingConversion [[nodiscard]] constexpr QuantityOf auto in(ToU) const @@ -266,7 +266,7 @@ public: return quantity{*this}; } - template ToU> + template requires detail::SaneScaling [[nodiscard]] constexpr QuantityOf auto force_in(ToU) const { @@ -280,7 +280,7 @@ public: return value_cast(*this); } - template ToRep, detail::WeakUnitOf ToU> + template ToRep, MP_UNITS_WEAK_UNIT_OF(quantity_spec) ToU> requires std::constructible_from && detail::SaneScaling [[nodiscard]] constexpr QuantityOf auto force_in(ToU) const { @@ -311,14 +311,14 @@ public: = delete; #endif - template U> + template requires detail::ValuePreservingScaling [[nodiscard]] constexpr rep numerical_value_in(U) const noexcept { return in(U{}).numerical_value_is_an_implementation_detail_; } - template U> + template requires detail::SaneScaling [[nodiscard]] constexpr rep force_numerical_value_in(U) const noexcept { diff --git a/src/core/include/mp-units/framework/quantity_cast.h b/src/core/include/mp-units/framework/quantity_cast.h index d184456d..018317eb 100644 --- a/src/core/include/mp-units/framework/quantity_cast.h +++ b/src/core/include/mp-units/framework/quantity_cast.h @@ -24,6 +24,7 @@ // IWYU pragma: private, include #include +#include #include #include #include @@ -57,7 +58,7 @@ namespace mp_units { * @tparam ToQS a quantity specification to use for a target quantity */ template> - requires(castable(Q::quantity_spec, ToQS)) && detail::WeakUnitOf + requires(castable(Q::quantity_spec, ToQS)) && (MP_UNITS_WEAK_UNIT_OF(MP_UNITS_NONCONST_TYPE(Q::unit), ToQS)) [[nodiscard]] constexpr Quantity auto quantity_cast(FwdQ&& q) { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, make_reference(ToQS, Q::unit)}; @@ -81,7 +82,7 @@ template> - requires(castable(QP::quantity_spec, ToQS)) && detail::WeakUnitOf + requires(castable(QP::quantity_spec, ToQS)) && (MP_UNITS_WEAK_UNIT_OF(MP_UNITS_NONCONST_TYPE(QP::unit), ToQS)) [[nodiscard]] constexpr QuantityPoint auto quantity_cast(FwdQP&& qp) { return QP{quantity_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index 91f79d40..812cb5fd 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -25,6 +25,7 @@ // IWYU pragma: private, include #include #include +#include #include #include #include @@ -328,7 +329,7 @@ public: } // unit conversions - template ToU> + template requires detail::ValuePreservingScaling [[nodiscard]] constexpr QuantityPointOf auto in(ToU) const { @@ -342,7 +343,7 @@ public: return ::mp_units::quantity_point{quantity_ref_from(point_origin).template in(), point_origin}; } - template ToRep, detail::WeakUnitOf ToU> + template ToRep, MP_UNITS_WEAK_UNIT_OF(quantity_spec) ToU> requires detail::ValuePreservingConstruction && detail::ValuePreservingConversion [[nodiscard]] constexpr QuantityPointOf auto in(ToU) const @@ -350,7 +351,7 @@ public: return ::mp_units::quantity_point{quantity_ref_from(point_origin).template in(ToU{}), point_origin}; } - template ToU> + template requires detail::SaneScaling [[nodiscard]] constexpr QuantityPointOf auto force_in(ToU) const { @@ -364,7 +365,7 @@ public: return ::mp_units::quantity_point{quantity_ref_from(point_origin).template force_in(), point_origin}; } - template ToRep, detail::WeakUnitOf ToU> + template ToRep, MP_UNITS_WEAK_UNIT_OF(quantity_spec) ToU> requires std::constructible_from && detail::SaneScaling [[nodiscard]] constexpr QuantityPointOf auto force_in(ToU) const { diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 724669ed..21883666 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -23,6 +23,7 @@ #pragma once // IWYU pragma: private, include +#include #include #include #include @@ -107,7 +108,7 @@ concept DerivedQuantitySpec = (QuantityKindSpec && is_specialization_of)); -template U> +template [[nodiscard]] consteval Reference auto make_reference(QS, U u) { if constexpr (requires { requires(mp_units::get_quantity_spec(U{}) == QS{}); }) diff --git a/src/core/include/mp-units/framework/reference.h b/src/core/include/mp-units/framework/reference.h index 69b0c233..3d83b54c 100644 --- a/src/core/include/mp-units/framework/reference.h +++ b/src/core/include/mp-units/framework/reference.h @@ -23,6 +23,7 @@ #pragma once // IWYU pragma: private, include +#include #include #include #include @@ -49,7 +50,7 @@ using reference_t = reference +template [[nodiscard]] consteval QuantitySpec auto get_quantity_spec(U) { return kind_of; @@ -81,7 +82,7 @@ struct reference { return Q{} == Q2{} && U{} == U2{}; } - template + template [[nodiscard]] friend consteval bool operator==(reference, U2 u2) { return Q{} == get_quantity_spec(u2) && U{} == u2; @@ -95,7 +96,7 @@ struct reference { return {}; } - template + template [[nodiscard]] friend consteval detail::reference_t<(MP_UNITS_EXPRESSION_WORKAROUND(Q{} * get_quantity_spec(U2{}))), MP_UNITS_EXPRESSION_WORKAROUND(U{} * U2{})> operator*(reference, U2) @@ -103,7 +104,7 @@ struct reference { return {}; } - template + template [[nodiscard]] friend consteval detail::reference_t operator*(U1, reference) @@ -119,7 +120,7 @@ struct reference { return {}; } - template + template [[nodiscard]] friend consteval detail::reference_t operator/(reference, U2) @@ -127,7 +128,7 @@ struct reference { return {}; } - template + template [[nodiscard]] friend consteval detail::reference_t operator/(U1, reference) @@ -266,8 +267,9 @@ template // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) constexpr auto operator/(R, Q&& q) = delete; -[[nodiscard]] consteval AssociatedUnit auto get_common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2, - AssociatedUnit auto... rest) +[[nodiscard]] consteval MP_UNITS_ASSOCIATED_UNIT auto get_common_reference(MP_UNITS_ASSOCIATED_UNIT auto u1, + MP_UNITS_ASSOCIATED_UNIT auto u2, + MP_UNITS_ASSOCIATED_UNIT auto... rest) requires requires { get_common_quantity_spec(get_quantity_spec(u1), get_quantity_spec(u2), get_quantity_spec(rest)...); get_common_unit(u1, u2, rest...); @@ -278,10 +280,12 @@ constexpr auto operator/(R, Q&& q) = delete; template [[nodiscard]] consteval Reference auto get_common_reference(R1 r1, R2 r2, Rest... rest) - requires(!(AssociatedUnit && AssociatedUnit && (... && AssociatedUnit))) && requires { - get_common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...); - get_common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...); - } + requires(!(MP_UNITS_ASSOCIATED_UNIT_T(R1) && MP_UNITS_ASSOCIATED_UNIT_T(R2) && + (... && MP_UNITS_ASSOCIATED_UNIT_T(Rest)))) && + requires { + get_common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...); + get_common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...); + } { return detail::reference_t +template [[nodiscard]] consteval MP_UNITS_REMOVE_CONST(decltype(To)) clone_reference_with(From) { return {}; diff --git a/src/core/include/mp-units/framework/reference_concepts.h b/src/core/include/mp-units/framework/reference_concepts.h index 2d346399..867f8b49 100644 --- a/src/core/include/mp-units/framework/reference_concepts.h +++ b/src/core/include/mp-units/framework/reference_concepts.h @@ -23,6 +23,7 @@ #pragma once // IWYU pragma: private, include +#include #include #include #include @@ -34,7 +35,7 @@ struct reference; MP_UNITS_EXPORT_BEGIN -template +template [[nodiscard]] consteval QuantitySpec auto get_quantity_spec(U); template @@ -43,7 +44,7 @@ template return Q{}; } -[[nodiscard]] consteval Unit auto get_unit(AssociatedUnit auto u) { return u; } +[[nodiscard]] consteval Unit auto get_unit(MP_UNITS_ASSOCIATED_UNIT auto u) { return u; } template [[nodiscard]] consteval Unit auto get_unit(reference) @@ -57,7 +58,7 @@ template * Satisfied by all specializations of @c reference. */ template -concept Reference = AssociatedUnit || is_specialization_of; +concept Reference = MP_UNITS_ASSOCIATED_UNIT_T(T) || is_specialization_of; /** * @brief A concept matching all references of the provided quantity spec diff --git a/src/core/include/mp-units/framework/system_reference.h b/src/core/include/mp-units/framework/system_reference.h index c3517917..bd408d64 100644 --- a/src/core/include/mp-units/framework/system_reference.h +++ b/src/core/include/mp-units/framework/system_reference.h @@ -23,12 +23,15 @@ #pragma once // IWYU pragma: private, include +#include #include #include #include #include #include +#if MP_UNITS_API_NATURAL_UNITS + MP_UNITS_EXPORT namespace mp_units { @@ -60,7 +63,7 @@ namespace mp_units { * @tparam CoU coherent unit for a quantity in this system */ template - requires(!AssociatedUnit) || (CoU == one) + requires(!MP_UNITS_ASSOCIATED_UNIT_T(decltype(CoU))) || (CoU == one) struct system_reference { static constexpr auto quantity_spec = Q; static constexpr auto coherent_unit = CoU; @@ -77,3 +80,5 @@ struct system_reference { }; } // namespace mp_units + +#endif // MP_UNITS_API_NATURAL_UNITS \ No newline at end of file diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 7c5b7367..b3fae4b5 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -23,6 +23,7 @@ #pragma once // IWYU pragma: private, include +#include #include #include #include @@ -316,6 +317,8 @@ struct named_unit : detail::unit_interface { static constexpr auto _point_origin_ = PO; }; +#if MP_UNITS_API_NATURAL_UNITS + /** * @brief Specialization for a unit that can be reused by several base quantities * @@ -333,6 +336,8 @@ struct named_unit : detail::unit_interface { static constexpr auto _symbol_ = Symbol; ///< Unique base unit identifier }; +#endif // MP_UNITS_API_NATURAL_UNITS + /** * @brief Specialization for a unit with special name * @@ -365,7 +370,7 @@ struct named_unit : decltype(U)::_base_type_ { * @tparam Unit a unit for which we provide a special name * @tparam QuantitySpec a specification of a quantity to be measured with this unit */ -template +template requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension) struct named_unit : decltype(U)::_base_type_ { using _base_type_ = named_unit; // exposition only @@ -373,7 +378,7 @@ struct named_unit : decltype(U)::_base_type_ { static constexpr auto _quantity_spec_ = QS; }; -template +template requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension) struct named_unit : decltype(U)::_base_type_ { using _base_type_ = named_unit; // exposition only diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index 61d92e3f..e43ebcfd 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -25,6 +25,7 @@ // IWYU pragma: private, include #include #include +#include #include #include #include @@ -54,6 +55,8 @@ struct named_unit; MP_UNITS_EXPORT template concept PrefixableUnit = Unit && is_derived_from_specialization_of_v; +#if MP_UNITS_API_NATURAL_UNITS + namespace detail { template @@ -91,6 +94,8 @@ template MP_UNITS_EXPORT template concept AssociatedUnit = Unit && detail::has_associated_quantity(U{}); +#endif + /** * @brief A concept matching all units associated with the provided quantity spec * @@ -99,17 +104,21 @@ concept AssociatedUnit = Unit && detail::has_associated_quantity(U{}); */ MP_UNITS_EXPORT template concept UnitOf = - AssociatedUnit && QuantitySpec && + MP_UNITS_ASSOCIATED_UNIT && QuantitySpec && (implicitly_convertible(get_quantity_spec(U{}), QS) || (unsatisfied<"Unit '{}' is associated with quantity of kind '{}' which is not convertible to the '{}' quantity">( U{}, type_name(get_quantity_spec(U{})._quantity_spec_), type_name(QS)))); namespace detail { +#if MP_UNITS_API_NATURAL_UNITS + template concept WeakUnitOf = Unit && QuantitySpec && ((!AssociatedUnit) || UnitOf); +#endif + template concept UnitsOfCompatibleQuantities = explicitly_convertible(get_quantity_spec(U1), get_quantity_spec(U2)) || @@ -126,9 +135,10 @@ concept ConvertibleUnits = (get_canonical_unit(U1).reference_unit == get_canonic template concept UnitConvertibleTo = Unit && Unit && - ((U1{} == U2) || ((!AssociatedUnit || !AssociatedUnit || - UnitsOfCompatibleQuantities) && - ConvertibleUnits)); + ((U1{} == U2) || + ((!MP_UNITS_ASSOCIATED_UNIT_T(U1) || !MP_UNITS_ASSOCIATED_UNIT_T(MP_UNITS_REMOVE_CONST(decltype(U2))) || + UnitsOfCompatibleQuantities) && + ConvertibleUnits)); template concept OffsetUnit = Unit && requires { T::_point_origin_; }; diff --git a/src/core/include/mp-units/framework/value_cast.h b/src/core/include/mp-units/framework/value_cast.h index 34b28f3e..6a975cdf 100644 --- a/src/core/include/mp-units/framework/value_cast.h +++ b/src/core/include/mp-units/framework/value_cast.h @@ -25,6 +25,7 @@ // IWYU pragma: private, include #include #include +#include #include #include #include @@ -75,8 +76,8 @@ concept SaneScaling = UnitConvertibleTo> - requires detail::WeakUnitOf && - detail::SaneScaling + requires(MP_UNITS_WEAK_UNIT_OF(MP_UNITS_REMOVE_CONST(decltype(ToU)), Q::quantity_spec)) && + detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { return detail::sudo_cast>( @@ -112,18 +113,18 @@ template> * @tparam ToRep a representation type to use for the target quantity */ template> - requires detail::WeakUnitOf && - RepresentationOf && std::constructible_from && - detail::SaneScaling + requires(MP_UNITS_WEAK_UNIT_OF(MP_UNITS_REMOVE_CONST(decltype(ToU)), Q::quantity_spec)) && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { return detail::sudo_cast>(std::forward(q)); } template> - requires detail::WeakUnitOf && - RepresentationOf && std::constructible_from && - detail::SaneScaling + requires(MP_UNITS_WEAK_UNIT_OF(MP_UNITS_REMOVE_CONST(decltype(ToU)), Q::quantity_spec)) && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { return value_cast(std::forward(q)); @@ -146,7 +147,7 @@ template> requires(ToQ::quantity_spec == Q::quantity_spec) && - detail::WeakUnitOf && + (MP_UNITS_WEAK_UNIT_OF(MP_UNITS_NONCONST_TYPE(ToQ::unit), Q::quantity_spec)) && std::constructible_from && detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) @@ -165,8 +166,8 @@ template> * @tparam ToU a unit to use for a target quantity point */ template> - requires detail::WeakUnitOf && - detail::SaneScaling + requires(MP_UNITS_WEAK_UNIT_OF(MP_UNITS_REMOVE_CONST(decltype(ToU)), QP::quantity_spec)) && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return quantity_point{value_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), @@ -203,9 +204,9 @@ template> - requires detail::WeakUnitOf && - RepresentationOf && std::constructible_from && - detail::SaneScaling + requires(MP_UNITS_WEAK_UNIT_OF(MP_UNITS_REMOVE_CONST(decltype(ToU)), QP::quantity_spec)) && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return quantity_point{ @@ -214,9 +215,9 @@ template> - requires detail::WeakUnitOf && - RepresentationOf && std::constructible_from && - detail::SaneScaling + requires(MP_UNITS_WEAK_UNIT_OF(MP_UNITS_REMOVE_CONST(decltype(ToU)), QP::quantity_spec)) && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return value_cast(std::forward(qp)); @@ -240,7 +241,7 @@ template> requires(ToQ::quantity_spec == QP::quantity_spec) && - detail::WeakUnitOf && + (MP_UNITS_WEAK_UNIT_OF(MP_UNITS_NONCONST_TYPE(ToQ::unit), QP::quantity_spec)) && std::constructible_from && detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) @@ -279,7 +280,7 @@ template> requires(ToQP::quantity_spec == QP::quantity_spec) && - detail::WeakUnitOf && + (MP_UNITS_WEAK_UNIT_OF(MP_UNITS_NONCONST_TYPE(ToQP::unit), QP::quantity_spec)) && (detail::same_absolute_point_origins(ToQP::point_origin, QP::point_origin)) && std::constructible_from && detail::SaneScaling diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index ae91637e..8b9014f1 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -411,11 +412,15 @@ template value_cast(representation_values::one() / q); } { - if constexpr (AssociatedUnit) { +#if MP_UNITS_API_NATURAL_UNITS + if constexpr (!MP_UNITS_ASSOCIATED_UNIT) + return (representation_values::one() * one).force_in(To * q.unit) / q; + else +#endif + { constexpr QuantitySpec auto qs = get_quantity_spec(To) * quantity::quantity_spec; return qs(representation_values::one() * one).force_in(To * q.unit) / q; - } else - return (representation_values::one() * one).force_in(To * q.unit) / q; + } } /** diff --git a/src/systems/CMakeLists.txt b/src/systems/CMakeLists.txt index b110b1d9..b87c9e47 100644 --- a/src/systems/CMakeLists.txt +++ b/src/systems/CMakeLists.txt @@ -51,13 +51,19 @@ add_mp_units_module( include/mp-units/systems/international.h include/mp-units/systems/isq.h include/mp-units/systems/isq_angle.h - include/mp-units/systems/natural.h include/mp-units/systems/si.h include/mp-units/systems/typographic.h include/mp-units/systems/usc.h MODULE_INTERFACE_UNIT mp-units-systems.cpp ) +if(${projectPrefix}API_NATURAL_UNITS) + target_sources( + mp-units-systems PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include FILES + include/mp-units/systems/natural.h + ) +endif() + if(NOT ${projectPrefix}API_FREESTANDING) target_sources( mp-units-systems diff --git a/src/systems/mp-units-systems.cpp b/src/systems/mp-units-systems.cpp index 0d86b1dd..7fc9d6ae 100644 --- a/src/systems/mp-units-systems.cpp +++ b/src/systems/mp-units-systems.cpp @@ -44,7 +44,9 @@ import std; #include #include #include +#if MP_UNITS_API_NATURAL_UNITS #include +#endif #include #include #include diff --git a/test/static/CMakeLists.txt b/test/static/CMakeLists.txt index a01c2fc1..4e72aa7b 100644 --- a/test/static/CMakeLists.txt +++ b/test/static/CMakeLists.txt @@ -48,7 +48,6 @@ add_library( isq_angle_test.cpp isq_test.cpp limits_test.cpp - natural_test.cpp prime_test.cpp quantity_point_test.cpp quantity_spec_test.cpp @@ -64,6 +63,10 @@ add_library( usc_test.cpp ) +if(${projectPrefix}API_NATURAL_UNITS) + target_sources(unit_tests_static PRIVATE natural_test.cpp) +endif() + if(NOT ${projectPrefix}API_FREESTANDING) target_sources(unit_tests_static PRIVATE fractional_exponent_quantity.cpp math_test.cpp) endif() diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index c74bb03e..0f62dc00 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include #include #include #include @@ -49,6 +50,7 @@ inline constexpr struct my_relative_origin final : relative_point_origin { } second; @@ -56,6 +58,7 @@ inline constexpr struct hour final : named_unit<"h", mag<3600> * second> { } hour; } // namespace nu +#endif // BaseDimension static_assert(detail::BaseDimension); @@ -190,8 +193,10 @@ static_assert(Unit); static_assert(Unit, struct si::second>>); static_assert(Unit>>); static_assert(Unit); +#if MP_UNITS_API_NATURAL_UNITS static_assert(Unit); static_assert(Unit); +#endif static_assert(!Unit>>); static_assert(!Unit>); static_assert(!Unit>); @@ -228,32 +233,38 @@ static_assert(!PrefixableUnit); static_assert(!PrefixableUnit); #endif -// AssociatedUnit -static_assert(AssociatedUnit); -static_assert(!AssociatedUnit); -static_assert(AssociatedUnit); -static_assert(AssociatedUnit>); -static_assert(AssociatedUnit); -static_assert(AssociatedUnit); -static_assert(AssociatedUnit * si::second)>); -static_assert(AssociatedUnit); -static_assert(AssociatedUnit(si::metre))>); -static_assert(AssociatedUnit); -static_assert(AssociatedUnit, struct si::second>>); -static_assert(AssociatedUnit>>); -static_assert(AssociatedUnit); -static_assert(AssociatedUnit / si::hour, si::metre / si::second))>); -static_assert(!AssociatedUnit); -static_assert(!AssociatedUnit / nu::hour, si::metre / nu::second))>); -static_assert(!AssociatedUnit>>); -static_assert(!AssociatedUnit>); -static_assert(!AssociatedUnit>); -static_assert(!AssociatedUnit>>); -static_assert(!AssociatedUnit, si::second>>); -static_assert(!AssociatedUnit); -static_assert(!AssociatedUnit); +// MP_UNITS_ASSOCIATED_UNIT +static_assert(MP_UNITS_ASSOCIATED_UNIT); +#if MP_UNITS_API_NATURAL_UNITS +static_assert(!MP_UNITS_ASSOCIATED_UNIT); +#endif +static_assert(MP_UNITS_ASSOCIATED_UNIT); +static_assert(MP_UNITS_ASSOCIATED_UNIT>); +static_assert(MP_UNITS_ASSOCIATED_UNIT); +static_assert(MP_UNITS_ASSOCIATED_UNIT); +static_assert(MP_UNITS_ASSOCIATED_UNIT * si::second)>); +static_assert(MP_UNITS_ASSOCIATED_UNIT); +static_assert(MP_UNITS_ASSOCIATED_UNIT(si::metre))>); +static_assert(MP_UNITS_ASSOCIATED_UNIT); +static_assert(MP_UNITS_ASSOCIATED_UNIT, struct si::second>>); +static_assert(MP_UNITS_ASSOCIATED_UNIT>>); +static_assert(MP_UNITS_ASSOCIATED_UNIT); +static_assert( + MP_UNITS_ASSOCIATED_UNIT / si::hour, si::metre / si::second))>); +#if MP_UNITS_API_NATURAL_UNITS +static_assert(!MP_UNITS_ASSOCIATED_UNIT); +static_assert( + !MP_UNITS_ASSOCIATED_UNIT / nu::hour, si::metre / nu::second))>); +#endif +static_assert(!MP_UNITS_ASSOCIATED_UNIT>>); +static_assert(!MP_UNITS_ASSOCIATED_UNIT>); +static_assert(!MP_UNITS_ASSOCIATED_UNIT>); +static_assert(!MP_UNITS_ASSOCIATED_UNIT>>); +static_assert(!MP_UNITS_ASSOCIATED_UNIT, si::second>>); +static_assert(!MP_UNITS_ASSOCIATED_UNIT); +static_assert(!MP_UNITS_ASSOCIATED_UNIT); #if MP_UNITS_HOSTED -static_assert(!AssociatedUnit); +static_assert(!MP_UNITS_ASSOCIATED_UNIT); #endif // UnitOf diff --git a/test/static/reference_test.cpp b/test/static/reference_test.cpp index 378a3bd5..dfd6387f 100644 --- a/test/static/reference_test.cpp +++ b/test/static/reference_test.cpp @@ -69,6 +69,7 @@ inline constexpr struct metre_ final : named_unit<"m", kind_of> {} metre inline constexpr struct gram_ final : named_unit<"g", kind_of> {} gram; inline constexpr auto kilogram = si::kilo; +#if MP_UNITS_API_NATURAL_UNITS namespace nu { // hypothetical natural system of units for c=1 @@ -80,6 +81,7 @@ inline constexpr struct length : system_reference {} length; inline constexpr struct speed : system_reference {} speed; } +#endif // derived named units inline constexpr struct radian_ final : named_unit<"rad", metre / metre, kind_of> {} radian; @@ -218,6 +220,7 @@ static_assert(is_of_type<120.L * length[kilometre] / (2 * time[hour]), static_assert(is_of_type<1. / 4 * area[square(metre)], decltype(1. * area[square(metre)] / 4)>); static_assert(1. / 4 * area[square(metre)] == 1. * area[square(metre)] / 4); +#if MP_UNITS_API_NATURAL_UNITS // Natural Units static_assert(is_of_type<42 * nu::time[nu::second], quantity{}, int>>); static_assert(is_of_type<42 * nu::time[nu::minute], quantity{}, int>>); @@ -251,6 +254,7 @@ static_assert(invalid_nu_unit); static_assert(invalid_nu_unit); static_assert(invalid_nu_unit); static_assert(invalid_nu_unit); +#endif // mixing associated units and references static_assert(second != time[second]); diff --git a/test/static/unit_test.cpp b/test/static/unit_test.cpp index 82022380..147ace3e 100644 --- a/test/static/unit_test.cpp +++ b/test/static/unit_test.cpp @@ -53,8 +53,10 @@ inline constexpr struct gram_ final : named_unit<"g", kind_of> {} gra inline constexpr auto kilogram = kilo; inline constexpr struct kelvin_ final : named_unit<"K", kind_of> {} kelvin; +#if MP_UNITS_API_NATURAL_UNITS // hypothetical natural units for c=1 inline constexpr struct nu_second_ final : named_unit<"s"> {} nu_second; +#endif // derived named units inline constexpr struct radian_ final : named_unit<"rad", metre / metre, kind_of> {} radian; @@ -89,7 +91,6 @@ inline constexpr struct speed_of_light_in_vacuum_ final : named_unit<"c", mag<29 // concepts verification static_assert(Unit); static_assert(Unit); -static_assert(Unit); static_assert(Unit); static_assert(Unit); static_assert(Unit); @@ -99,10 +100,13 @@ static_assert(Unit); static_assert(Unit); static_assert(Unit * second)>); static_assert(Unit); -static_assert(Unit); static_assert(Unit); -static_assert(Unit); static_assert(Unit); +#if MP_UNITS_API_NATURAL_UNITS +static_assert(Unit); +static_assert(Unit); +static_assert(Unit); +#endif static_assert(PrefixableUnit); static_assert(PrefixableUnit); @@ -171,7 +175,9 @@ static_assert(get_canonical_unit(joule).mag == mag<1000>); // !!! (because of k static_assert(joule == joule); static_assert(joule != newton); +#if MP_UNITS_API_NATURAL_UNITS static_assert(is_of_type); +#endif // constant_unit static_assert(is_of_type);