diff --git a/example/include/ranged_representation.h b/example/include/ranged_representation.h index 0ffd5d70d..1643ec68b 100644 --- a/example/include/ranged_representation.h +++ b/example/include/ranged_representation.h @@ -62,9 +62,6 @@ class ranged_representation : public validated_type -constexpr bool mp_units::is_scalar> = mp_units::is_scalar; - template struct MP_UNITS_STD_FMT::formatter, Char> : formatter { template diff --git a/example/include/validated_type.h b/example/include/validated_type.h index 70be6dac3..67ae53354 100644 --- a/example/include/validated_type.h +++ b/example/include/validated_type.h @@ -113,9 +113,6 @@ class validated_type { = default; }; -template -constexpr bool mp_units::is_scalar> = mp_units::is_scalar; - template std::basic_ostream& operator<<(std::basic_ostream& os, diff --git a/example/kalman_filter/kalman_filter-example_2.cpp b/example/kalman_filter/kalman_filter-example_2.cpp index 5a07edebd..9a3c562c7 100644 --- a/example/kalman_filter/kalman_filter-example_2.cpp +++ b/example/kalman_filter/kalman_filter-example_2.cpp @@ -38,10 +38,6 @@ import mp_units; // Based on: https://www.kalmanfilter.net/alphabeta.html#ex2 -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - using namespace mp_units; void print_header(const kalman::SystemState auto& initial) diff --git a/example/kalman_filter/kalman_filter-example_3.cpp b/example/kalman_filter/kalman_filter-example_3.cpp index 65aa2b1f2..437ffc172 100644 --- a/example/kalman_filter/kalman_filter-example_3.cpp +++ b/example/kalman_filter/kalman_filter-example_3.cpp @@ -38,10 +38,6 @@ import mp_units; // Based on: https://www.kalmanfilter.net/alphabeta.html#ex3 -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - using namespace mp_units; void print_header(const kalman::SystemState auto& initial) diff --git a/example/kalman_filter/kalman_filter-example_4.cpp b/example/kalman_filter/kalman_filter-example_4.cpp index 64e240e1f..d28b591fb 100644 --- a/example/kalman_filter/kalman_filter-example_4.cpp +++ b/example/kalman_filter/kalman_filter-example_4.cpp @@ -38,10 +38,6 @@ import mp_units; // Based on: https://www.kalmanfilter.net/alphabeta.html#ex4 -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - using namespace mp_units; void print_header(const kalman::SystemState auto& initial) diff --git a/example/measurement.cpp b/example/measurement.cpp index 596361132..1f291cee2 100644 --- a/example/measurement.cpp +++ b/example/measurement.cpp @@ -124,6 +124,13 @@ class measurement { return os << v.value() << " ± " << v.uncertainty(); } + [[nodiscard]] friend constexpr measurement abs(const measurement& v) + requires requires { abs(v.value()); } || requires { std::abs(v.value()); } + { + using std::abs; + return measurement(abs(v.value()), v.uncertainty()); + } + private: value_type value_{}; value_type uncertainty_{}; @@ -131,12 +138,8 @@ class measurement { } // namespace -template -constexpr bool mp_units::is_scalar> = true; -template -constexpr bool mp_units::is_vector> = true; - static_assert(mp_units::RepresentationOf, mp_units::quantity_character::scalar>); +static_assert(mp_units::RepresentationOf, mp_units::quantity_character::vector>); namespace { diff --git a/example/storage_tank.cpp b/example/storage_tank.cpp index 86bc32ffa..e733f56f4 100644 --- a/example/storage_tank.cpp +++ b/example/storage_tank.cpp @@ -40,12 +40,6 @@ import mp_units; #include #endif -// allows standard gravity (acceleration) and weight (force) to be expressed with scalar representation -// types instead of requiring the usage of Linear Algebra library for this simple example -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - namespace { using namespace mp_units; diff --git a/example/strong_angular_quantities.cpp b/example/strong_angular_quantities.cpp index 9c03b36c1..88b80abe0 100644 --- a/example/strong_angular_quantities.cpp +++ b/example/strong_angular_quantities.cpp @@ -33,10 +33,6 @@ import mp_units; #include #endif -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - int main() { using namespace mp_units; diff --git a/example/total_energy.cpp b/example/total_energy.cpp index 75416bb7f..eced87bba 100644 --- a/example/total_energy.cpp +++ b/example/total_energy.cpp @@ -37,10 +37,6 @@ import mp_units; #include #endif -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - namespace { using namespace mp_units; 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 2282ffb2d..dc7b4211b 100644 --- a/src/core/include/mp-units/bits/get_associated_quantity.h +++ b/src/core/include/mp-units/bits/get_associated_quantity.h @@ -80,7 +80,7 @@ template else if constexpr (requires { U::_reference_unit_; }) return determine_associated_quantity(U::_reference_unit_); else if constexpr (requires { typename U::_num_; }) { - return expr_map(u); + return expr_map(u); } } diff --git a/src/core/include/mp-units/bits/text_tools.h b/src/core/include/mp-units/bits/text_tools.h index f14e0f7d7..4af3762f2 100644 --- a/src/core/include/mp-units/bits/text_tools.h +++ b/src/core/include/mp-units/bits/text_tools.h @@ -33,8 +33,11 @@ import std; #else #include -#endif -#endif +#if __cpp_lib_text_encoding +#include +#endif // __cpp_lib_text_encoding +#endif // MP_UNITS_IMPORT_STD +#endif // MP_UNITS_IN_MODULE_INTERFACE namespace mp_units::detail { @@ -102,6 +105,11 @@ constexpr Out copy(const symbol_text& txt, character_set char_set, Out out if constexpr (is_same_v) return ::mp_units::detail::copy(txt.utf8().begin(), txt.utf8().end(), out); else if constexpr (is_same_v) { +#if __cpp_lib_text_encoding + if (std::text_encoding::literal().mib() != std::text_encoding::id::UTF8) + // fallback to portable mode + return ::mp_units::detail::copy(txt.portable().begin(), txt.portable().end(), out); +#endif for (const char8_t ch : txt.utf8()) *out++ = static_cast(ch); return out; } else diff --git a/src/core/include/mp-units/cartesian_vector.h b/src/core/include/mp-units/cartesian_vector.h index af5e7e366..4af2679ab 100644 --- a/src/core/include/mp-units/cartesian_vector.h +++ b/src/core/include/mp-units/cartesian_vector.h @@ -215,7 +215,7 @@ class cartesian_vector { lhs._coordinates_[2] == rhs._coordinates_[2]; } - [[nodiscard]] friend constexpr T norm(const cartesian_vector& vec) + [[nodiscard]] friend constexpr T magnitude(const cartesian_vector& vec) requires treat_as_floating_point { return vec.magnitude(); @@ -263,9 +263,6 @@ template requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t; } cartesian_vector(Arg, Args...) -> cartesian_vector>; -template -constexpr bool is_vector> = true; - } // namespace mp_units #if MP_UNITS_HOSTED diff --git a/src/core/include/mp-units/complex.h b/src/core/include/mp-units/complex.h index 9f09c05f7..537fbcc87 100644 --- a/src/core/include/mp-units/complex.h +++ b/src/core/include/mp-units/complex.h @@ -25,7 +25,7 @@ #include // #include -#include +#include #ifndef MP_UNITS_IN_MODULE_INTERFACE #ifdef MP_UNITS_IMPORT_STD @@ -38,6 +38,6 @@ import std; namespace mp_units { template -constexpr bool is_complex> = true; +constexpr bool disable_scalar> = true; -} +} // namespace mp_units diff --git a/src/core/include/mp-units/ext/type_name.h b/src/core/include/mp-units/ext/type_name.h index d368f869c..403ecd65c 100644 --- a/src/core/include/mp-units/ext/type_name.h +++ b/src/core/include/mp-units/ext/type_name.h @@ -10,6 +10,7 @@ import std; #else #include +#include #endif #endif @@ -37,6 +38,8 @@ template return name; } +template +struct type_name_less : std::bool_constant() < type_name()> {}; // This is typically used to deterministically chose one of the alternatives // to guarantee the commutation of the operation (e.g., `a + b` should return diff --git a/src/core/include/mp-units/framework/customization_points.h b/src/core/include/mp-units/framework/customization_points.h index 43f8cfff2..d33b111d2 100644 --- a/src/core/include/mp-units/framework/customization_points.h +++ b/src/core/include/mp-units/framework/customization_points.h @@ -60,157 +60,22 @@ constexpr bool treat_as_floating_point = std::is_floating_point_v>; #endif -/** - * @brief Specifies a type to have a scalar character - * - * A scalar is a physical quantity that has magnitude but no direction. - */ template -constexpr bool is_scalar = std::is_floating_point_v || (std::is_integral_v && !is_same_v); +[[deprecated("`is_scalar` is no longer necessary and can simply be removed")]] +constexpr bool is_scalar = false; -/** - * @brief Specifies a type to have a complex character - * - * A complex is a physical quantity that has a complex representation type. - */ template +[[deprecated("`is_complex` is no longer necessary and can simply be removed")]] constexpr bool is_complex = false; -/** - * @brief Specifies a type to have a vector character - * - * Vectors are physical quantities that possess both magnitude and direction - * and whose operations obey the axioms of a vector space. - * - * In specific cases a scalar can represent a vector with the default direction. - * If that is the intent, a user should provide a partial specialization: - * - * @code{.cpp} - * template - * requires mp_units::is_scalar - * constexpr bool mp_units::is_vector = true; - * @endcode - */ template +[[deprecated("`is_vector` is no longer necessary and can simply be removed")]] constexpr bool is_vector = false; -/** - * @brief Specifies a type to have a tensor character - * - * Tensors can be used to describe more general physical quantities. - * - * A vector is a tensor of the first order and a scalar is a tensor of order zero. - * Similarly to `is_vector` a partial specialization is needed in such cases. - */ template +[[deprecated("`is_tensor` is no longer necessary and can simply be removed")]] constexpr bool is_tensor = false; -MP_UNITS_EXPORT_END - -namespace detail::norm_impl { - -void norm() = delete; // poison pill - -struct norm_t { - template - [[nodiscard]] constexpr auto operator()(const T& vec) const - { - if constexpr (requires { vec.norm(); }) - return vec.norm(); - else if constexpr (requires { norm(vec); }) - return norm(vec); - else if constexpr (requires { vec.magnitude(); }) - return vec.magnitude(); - else if constexpr (requires { magnitude(vec); }) - return magnitude(vec); - // TODO Is it a good idea to enable fundamental types to represent vector quantities? - // else if constexpr (is_scalar) - // return std::abs(vec); - } -}; - -} // namespace detail::norm_impl - -inline namespace cpo { - -MP_UNITS_EXPORT inline constexpr ::mp_units::detail::norm_impl::norm_t norm; - -} - -namespace detail::real_impl { - -void real() = delete; // poison pill - -struct real_t { - template - [[nodiscard]] constexpr auto operator()(const T& clx) const - { - if constexpr (requires { clx.real(); }) - return clx.real(); - else if constexpr (requires { real(clx); }) - return real(clx); - } -}; - -} // namespace detail::real_impl - -inline namespace cpo { - -MP_UNITS_EXPORT inline constexpr ::mp_units::detail::real_impl::real_t real; - -} - -namespace detail::imag_impl { - -void imag() = delete; // poison pill - -struct imag_t { - template - [[nodiscard]] constexpr auto operator()(const T& clx) const - { - if constexpr (requires { clx.imag(); }) - return clx.imag(); - else if constexpr (requires { imag(clx); }) - return imag(clx); - } -}; - -} // namespace detail::imag_impl - -inline namespace cpo { - -MP_UNITS_EXPORT inline constexpr ::mp_units::detail::imag_impl::imag_t imag; - -} - -namespace detail::modulus_impl { - -void modulus() = delete; // poison pill - -struct modulus_t { - template - [[nodiscard]] constexpr auto operator()(const T& clx) const - { - if constexpr (requires { clx.modulus(); }) - return clx.modulus(); - else if constexpr (requires { modulus(clx); }) - return modulus(clx); - // `std` made a precedence of using `abs` for modulo on `std::complex` - else if constexpr (requires { abs(clx); }) - return abs(clx); - } -}; - -} // namespace detail::modulus_impl - -inline namespace cpo { - -MP_UNITS_EXPORT inline constexpr ::mp_units::detail::modulus_impl::modulus_t modulus; - -} - -MP_UNITS_EXPORT_BEGIN - /** * @brief A type trait that defines zero, one, min, and max for a representation type * @@ -222,9 +87,9 @@ MP_UNITS_EXPORT_BEGIN */ template #if MP_UNITS_HOSTED -struct quantity_values : std::chrono::duration_values { +struct representation_values : std::chrono::duration_values { #else -struct quantity_values { +struct representation_values { static constexpr Rep zero() noexcept requires std::constructible_from { @@ -255,6 +120,9 @@ struct quantity_values { } }; +template +using quantity_values [[deprecated("Use `representation_values` instead")]] = representation_values; + /** * @brief Provides support for external quantity-like types * diff --git a/src/core/include/mp-units/framework/dimension.h b/src/core/include/mp-units/framework/dimension.h index b0ff804f6..21c77929e 100644 --- a/src/core/include/mp-units/framework/dimension.h +++ b/src/core/include/mp-units/framework/dimension.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -59,12 +58,6 @@ MP_UNITS_EXPORT struct dimension_one; namespace detail { -template -struct base_dimension_less : std::bool_constant() < type_name()> {}; - -template -using type_list_of_base_dimension_less = expr_less; - template struct derived_dimension_impl : expr_fractions {}; @@ -72,13 +65,13 @@ struct dimension_interface { template [[nodiscard]] friend consteval Dimension auto operator*(Lhs, Rhs) { - return expr_multiply(Lhs{}, Rhs{}); + return expr_multiply(Lhs{}, Rhs{}); } template [[nodiscard]] friend consteval Dimension auto operator/(Lhs, Rhs) { - return expr_divide(Lhs{}, Rhs{}); + return expr_divide(Lhs{}, Rhs{}); } template @@ -197,8 +190,7 @@ template requires detail::non_zero [[nodiscard]] consteval Dimension auto pow(D d) { - return detail::expr_pow( - d); + return detail::expr_pow(d); } /** diff --git a/src/core/include/mp-units/framework/expression_template.h b/src/core/include/mp-units/framework/expression_template.h index 891396ab6..a0e0bc61e 100644 --- a/src/core/include/mp-units/framework/expression_template.h +++ b/src/core/include/mp-units/framework/expression_template.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #ifndef MP_UNITS_IN_MODULE_INTERFACE @@ -117,8 +118,8 @@ constexpr bool ratio_one = true; template requires(detail::valid_ratio && detail::positive_ratio && !detail::ratio_one) struct power final { - using factor = F; - static constexpr detail::ratio exponent{Num, Den...}; + using _factor_ = F; + static constexpr detail::ratio _exponent_{Num, Den...}; }; namespace detail { @@ -146,7 +147,7 @@ template consteval auto power_or_T_impl() { if constexpr (is_specialization_of_power) { - return power_or_T_impl(); + return power_or_T_impl(); } else { if constexpr (R.den == 1) { if constexpr (R.num == 1) @@ -198,13 +199,13 @@ struct expr_consolidate_impl> { // replaces the instance of a type and a power of it with one with incremented power template struct expr_consolidate_impl, Rest...>> { - using type = expr_consolidate_impl::exponent + 1>, Rest...>>::type; + using type = expr_consolidate_impl::_exponent_ + 1>, Rest...>>::type; }; // accumulates the powers of instances of the same type (removes the element in case the accumulation result is `0`) template struct expr_consolidate_impl, power, Rest...>> { - static constexpr ratio r = power::exponent + power::exponent; + static constexpr ratio r = power::_exponent_ + power::_exponent_; using type = expr_consolidate_impl, Rest...>>::type; }; @@ -260,7 +261,7 @@ struct expr_simplify_power { template typename Pred> struct expr_simplify, NRest...>, type_list, Pred> { using impl = expr_simplify, type_list, Pred>; - using type = expr_simplify_power::exponent, ratio{1}>; + using type = expr_simplify_power::_exponent_, ratio{1}>; using num = type_list_join; using den = type_list_join; }; @@ -269,7 +270,7 @@ struct expr_simplify, NRest...>, type_list typename Pred> struct expr_simplify, type_list, DRest...>, Pred> { using impl = expr_simplify, type_list, Pred>; - using type = expr_simplify_power::exponent>; + using type = expr_simplify_power::_exponent_>; using num = type_list_join; using den = type_list_join; }; @@ -280,7 +281,7 @@ template, power>) struct expr_simplify, NRest...>, type_list, DRest...>, Pred> { using impl = expr_simplify, type_list, Pred>; - using type = expr_simplify_power::exponent, power::exponent>; + using type = expr_simplify_power::_exponent_, power::_exponent_>; using num = type_list_join; using den = type_list_join; }; @@ -302,6 +303,8 @@ struct expr_less_impl, Pred> : std::true_type {}; template typename Pred> using expr_less = expr_less_impl; +template +using type_list_name_less = expr_less; // expr_fractions template, typename Den = type_list<>> @@ -387,8 +390,8 @@ template typename To, SymbolicArg OneType, template typename Pred, - typename Lhs, typename Rhs> +template typename To, SymbolicArg OneType, + template typename Pred = type_list_name_less, typename Lhs, typename Rhs> [[nodiscard]] MP_UNITS_CONSTEVAL auto expr_multiply(Lhs, Rhs) { if constexpr (is_same_v) { @@ -422,8 +425,8 @@ template typename To, SymbolicArg OneType, template typename To, SymbolicArg OneType, template typename Pred, - typename Lhs, typename Rhs> +template typename To, SymbolicArg OneType, + template typename Pred = type_list_name_less, typename Lhs, typename Rhs> [[nodiscard]] MP_UNITS_CONSTEVAL auto expr_divide(Lhs lhs, Rhs rhs) { if constexpr (is_same_v) { @@ -489,7 +492,7 @@ template typename To * @tparam T Expression being the base of the operation */ template typename To, SymbolicArg OneType, - template typename Pred, typename T> + template typename Pred = type_list_name_less, typename T> requires detail::non_zero [[nodiscard]] consteval auto expr_pow(T v) { @@ -552,7 +555,7 @@ template typename Proj, template typename To, Sy * @tparam T expression template to map from */ template typename Proj, template typename To, SymbolicArg OneType, - template typename Pred, typename T> + template typename Pred = type_list_name_less, typename T> [[nodiscard]] consteval auto expr_map(T) { if constexpr (type_list_size + type_list_size == 0) diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index ec6f26bfa..3d829769e 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -143,27 +143,27 @@ class quantity { // static member functions [[nodiscard]] static constexpr quantity zero() noexcept - requires requires { quantity_values::zero(); } + requires requires { representation_values::zero(); } { - return {quantity_values::zero(), R}; + return {representation_values::zero(), R}; } [[nodiscard]] static constexpr quantity one() noexcept - requires requires { quantity_values::one(); } + requires requires { representation_values::one(); } { - return {quantity_values::one(), R}; + return {representation_values::one(), R}; } [[nodiscard]] static constexpr quantity min() noexcept - requires requires { quantity_values::min(); } + requires requires { representation_values::min(); } { - return {quantity_values::min(), R}; + return {representation_values::min(), R}; } [[nodiscard]] static constexpr quantity max() noexcept - requires requires { quantity_values::max(); } + requires requires { representation_values::max(); } { - return {quantity_values::max(), R}; + return {representation_values::max(), R}; } // construction, assignment, destruction @@ -446,7 +446,7 @@ class quantity { } friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& val) { - MP_UNITS_EXPECTS_DEBUG(val != quantity_values::zero()); + MP_UNITS_EXPECTS_DEBUG(val != representation_values::zero()); // TODO use /= when compiler bug is resolved: // https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445 lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / val; @@ -577,7 +577,7 @@ class quantity { (!Reference) && detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] friend constexpr QuantityOf auto operator/(const Q& q, const Value& val) { - MP_UNITS_EXPECTS_DEBUG(val != quantity_values::zero()); + MP_UNITS_EXPECTS_DEBUG(val != representation_values::zero()); return ::mp_units::quantity{q.numerical_value_ref_in(unit) / val, R}; } diff --git a/src/core/include/mp-units/framework/quantity_concepts.h b/src/core/include/mp-units/framework/quantity_concepts.h index 275763ce7..c4bba5e32 100644 --- a/src/core/include/mp-units/framework/quantity_concepts.h +++ b/src/core/include/mp-units/framework/quantity_concepts.h @@ -43,6 +43,10 @@ template constexpr bool is_derived_from_specialization_of_quantity = requires(T* type) { to_base_specialization_of_quantity(type); }; +template + requires is_derived_from_specialization_of_quantity +constexpr bool is_quantity = true; + } // namespace detail /** diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index c0395069d..54437067c 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -106,12 +106,6 @@ template return ch; } -template -struct quantity_spec_less : std::bool_constant() < type_name()> {}; - -template -using type_list_of_quantity_spec_less = expr_less; - template requires requires { Q::dimension; } using to_dimension = MP_UNITS_NONCONST_TYPE(Q::dimension); @@ -127,16 +121,14 @@ struct quantity_spec_interface_base { [[nodiscard]] friend consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs) { return clone_kind_of( - expr_multiply(remove_kind(lhs), - remove_kind(rhs))); + expr_multiply(remove_kind(lhs), remove_kind(rhs))); } template [[nodiscard]] friend consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs) { return clone_kind_of( - expr_divide(remove_kind(lhs), - remove_kind(rhs))); + expr_divide(remove_kind(lhs), remove_kind(rhs))); } template @@ -433,8 +425,7 @@ struct derived_quantity_spec_impl : using _base_type_ = derived_quantity_spec_impl; using _base_ = expr_fractions; - static constexpr Dimension auto dimension = - expr_map(_base_{}); + static constexpr Dimension auto dimension = expr_map(_base_{}); static constexpr quantity_character character = derived_quantity_character(typename _base_::_num_{}, typename _base_::_den_{}); }; @@ -563,8 +554,7 @@ template [[nodiscard]] consteval QuantitySpec auto pow(Q q) { return detail::clone_kind_of( - detail::expr_pow( - detail::remove_kind(q))); + detail::expr_pow(detail::remove_kind(q))); } @@ -703,7 +693,7 @@ template requires requires { Q::_equation_; } [[nodiscard]] consteval auto explode_to_equation(power) { - constexpr ratio exp = power::exponent; + constexpr ratio exp = power::_exponent_; return explode_to_equation_result{ pow(Q::_equation_), defines_equation(Q{}) ? specs_convertible_result::yes : specs_convertible_result::explicit_conversion}; @@ -917,13 +907,13 @@ template constexpr auto qto = map_power(To{}); if constexpr (get_kind_tree_root(qfrom) == get_kind_tree_root(qto)) { if constexpr (is_specialization_of_power && is_specialization_of_power) - return extract_results{true, typename From::factor{}, typename To::factor{}, prepend_rest::no}; + return extract_results{true, typename From::_factor_{}, typename To::_factor_{}, prepend_rest::no}; else return extract_results{true, qfrom, qto, prepend_rest::no}; } else { auto normalize = [](Q) { if constexpr (is_specialization_of_power) - return std::tuple{typename Q::factor{}, Q::exponent}; + return std::tuple{typename Q::_factor_{}, Q::_exponent_}; else return std::tuple{Q{}, ratio{1}}; }; @@ -1526,7 +1516,7 @@ template } else if constexpr (requires { Q::_parent_; }) { return get_kind_tree_root(Q::_parent_); } else if constexpr (DerivedQuantitySpec) { - return expr_map(q); + return expr_map(q); } else { // root quantity return q; diff --git a/src/core/include/mp-units/framework/representation_concepts.h b/src/core/include/mp-units/framework/representation_concepts.h index 66a6db2ff..414c08752 100644 --- a/src/core/include/mp-units/framework/representation_concepts.h +++ b/src/core/include/mp-units/framework/representation_concepts.h @@ -33,10 +33,11 @@ import std; #else #include #include +#include #include #include -#endif -#endif +#endif // MP_UNITS_IMPORT_STD +#endif // MP_UNITS_IN_MODULE_INTERFACE namespace mp_units { @@ -62,105 +63,244 @@ namespace mp_units { */ MP_UNITS_EXPORT enum class quantity_character : std::int8_t { scalar, complex, vector, tensor }; +MP_UNITS_EXPORT template +constexpr bool disable_scalar = false; + +template<> +MP_UNITS_INLINE constexpr bool disable_scalar = true; + namespace detail { template concept WeaklyRegular = std::copyable && std::equality_comparable; template -concept Scalar = is_scalar; +concept Scalar = (!disable_scalar) && WeaklyRegular && requires(T a, T b) { + // scalar operations + { -a } -> std::common_with; + { a + b } -> std::common_with; + { a - b } -> std::common_with; + { a* b } -> std::common_with; + { a / b } -> std::common_with; +}; -template -concept Complex = is_complex; +namespace real_impl { + +void real() = delete; // poison pill + +struct real_t { + [[nodiscard]] constexpr Scalar auto operator()(const WeaklyRegular auto& clx) const + requires requires { clx.real(); } || requires { real(clx); } + { + if constexpr (requires { clx.real(); }) + return clx.real(); + else if constexpr (requires { real(clx); }) + return real(clx); + } +}; + +} // namespace real_impl + +} // namespace detail + +inline namespace cpo { + +MP_UNITS_EXPORT inline constexpr ::mp_units::detail::real_impl::real_t real; + +} + +namespace detail::imag_impl { + +void imag() = delete; // poison pill + +struct imag_t { + [[nodiscard]] constexpr Scalar auto operator()(const WeaklyRegular auto& clx) const + requires requires { clx.imag(); } || requires { imag(clx); } + { + if constexpr (requires { clx.imag(); }) + return clx.imag(); + else if constexpr (requires { imag(clx); }) + return imag(clx); + } +}; + +} // namespace detail::imag_impl + +inline namespace cpo { + +MP_UNITS_EXPORT inline constexpr ::mp_units::detail::imag_impl::imag_t imag; + +} + +namespace detail::modulus_impl { + +void modulus() = delete; // poison pill +void abs() = delete; // poison pill + +struct modulus_t { + [[nodiscard]] constexpr Scalar auto operator()(const WeaklyRegular auto& clx) const + requires requires { clx.modulus(); } || requires { modulus(clx); } || requires { clx.abs(); } || + requires { abs(clx); } + { + if constexpr (requires { clx.modulus(); }) + return clx.modulus(); + else if constexpr (requires { modulus(clx); }) + return modulus(clx); + // `std` made a precedence of using `abs` for modulo on `std::complex` + else if constexpr (requires { clx.abs(); }) + return clx.abs(); + else if constexpr (requires { abs(clx); }) + return abs(clx); + } +}; + +} // namespace detail::modulus_impl + +inline namespace cpo { + +MP_UNITS_EXPORT inline constexpr ::mp_units::detail::modulus_impl::modulus_t modulus; + +} + +MP_UNITS_EXPORT template +constexpr bool disable_complex = false; + +namespace detail { template -concept Vector = is_vector; +concept Complex = + (!disable_complex) && WeaklyRegular && Scalar> && + std::constructible_from, value_type_t> && requires(T a, T b, value_type_t s) { + // complex operations + { -a } -> std::common_with; + { a + b } -> std::common_with; + { a - b } -> std::common_with; + { a* b } -> std::common_with; + { a / b } -> std::common_with; + { a* s } -> std::common_with; + { s* a } -> std::common_with; + { a / s } -> std::common_with; + ::mp_units::real(a); + ::mp_units::imag(a); + ::mp_units::modulus(a); + }; + +namespace magnitude_impl { + +void magnitude() = delete; // poison pill +void abs() = delete; // poison pill + +struct magnitude_t { + template + [[nodiscard]] constexpr Scalar auto operator()(const T& vec) const + requires requires { vec.magnitude(); } || requires { magnitude(vec); } || + (Scalar && + (requires { vec.abs(); } || requires { abs(vec); } || (std::is_arithmetic_v && (!is_same_v)))) + { + if constexpr (requires { vec.magnitude(); }) + return vec.magnitude(); + else if constexpr (requires { magnitude(vec); }) + return magnitude(vec); + // allow scalar types to represent one dimensional vector quantities + if constexpr (Scalar) { + if constexpr (requires { vec.abs(); }) + return vec.abs(); + else if constexpr (requires { abs(vec); }) + return abs(vec); + else if constexpr (std::is_arithmetic_v && (!is_same_v)) +#if MP_UNITS_HOSTED || __cpp_lib_freestanding_cstdlib >= 202306L + return std::abs(vec); +#else + return vec >= 0 ? vec : -vec; +#endif + } + } +}; + +} // namespace magnitude_impl + +} // namespace detail + +inline namespace cpo { + +MP_UNITS_EXPORT inline constexpr ::mp_units::detail::magnitude_impl::magnitude_t magnitude; + +} + +MP_UNITS_EXPORT template +constexpr bool disable_vector = false; + +namespace detail { template -concept Tensor = is_tensor; +concept Vector = + (!disable_vector) && WeaklyRegular && Scalar> && requires(T a, T b, value_type_t s) { + // vector operations + { -a } -> std::common_with; + { a + b } -> std::common_with; + { a - b } -> std::common_with; + { a* s } -> std::common_with; + { s* a } -> std::common_with; + { a / s } -> std::common_with; + ::mp_units::magnitude(a); + // TODO should we check for the below as well (e.g., when `size() > 1` or `2`) + // { zero_vector() } -> Vector; + // { unit_vector(a) } -> Vector; + // { scalar_product(a, b) } -> Scalar; + // { vector_product(a, b) } -> Vector; + // { tensor_product(a, b) } -> Tensor2; + }; -template -concept IsOfCharacter = - (Ch == quantity_character::scalar && is_scalar) || (Ch == quantity_character::complex && is_complex) || - (Ch == quantity_character::vector && is_vector) || (Ch == quantity_character::tensor && is_tensor); +// TODO provide when some actual operations will be required +// template +// concept Tensor = is_tensor && WeaklyRegular; // && requires(T a, T b) { +// // tensor operations +// // { tensor_product(a, b) } -> Tensor4; +// // { inner_product(a, b) } -> Tensor2; +// // { scalar_product(a, b) } -> Scalar; +// //}; + +template +constexpr bool is_quantity = false; template using scaling_factor_type_t = conditional, long double, std::intmax_t>; template -concept ScalarRepresentation = Scalar && WeaklyRegular && requires(T a, T b, scaling_factor_type_t f) { +concept ScalarRepresentation = (!is_quantity) && Scalar && requires(T a, T b, scaling_factor_type_t f) { // scaling - { a* f } -> Scalar; - { f* a } -> Scalar; - { a / f } -> Scalar; - - // scalar operations - { -a } -> Scalar; - { a + b } -> Scalar; - { a - b } -> Scalar; - { a* b } -> Scalar; - { a / b } -> Scalar; + { a* f } -> std::common_with; + { f* a } -> std::common_with; + { a / f } -> std::common_with; }; template -concept ComplexRepresentation = Complex && WeaklyRegular && requires(T a, T b, scaling_factor_type_t f) { +concept ComplexRepresentation = (!is_quantity) && Complex && requires(T a, T b, scaling_factor_type_t f) { // scaling // TODO The below conversion to `T` is an exception compared to other representation types // `std::complex` * `U` do not work, but `std::complex` is convertible from `U` // Maybe expose this as a customization point? - { a* T(f) } -> Complex; - { T(f) * a } -> Complex; - { a / T(f) } -> Complex; - - // complex operations - { -a } -> Complex; - { a + b } -> Complex; - { a - b } -> Complex; - { a* b } -> Complex; - { a / b } -> Complex; - { ::mp_units::real(a) } -> Scalar; - { ::mp_units::imag(a) } -> Scalar; - { ::mp_units::modulus(a) } -> Scalar; + { a* T(f) } -> std::common_with; + { T(f) * a } -> std::common_with; + { a / T(f) } -> std::common_with; }; -// TODO how to check for a complex(Scalar, Scalar) -> Complex? - template -concept VectorRepresentation = Vector && WeaklyRegular && requires(T a, T b, scaling_factor_type_t f) { - // scaling - { a* f } -> Vector; - { f* a } -> Vector; - { a / f } -> Vector; - - // vector operations - { -a } -> Vector; - { a + b } -> Vector; - { a - b } -> Vector; - { ::mp_units::norm(a) } -> Scalar; - // TBD - // { zero_vector() } -> Vector; - // { unit_vector(a) } -> Vector; - // { scalar_product(a, b) } -> Scalar; - // { vector_product(a, b) } -> Vector; - // { tensor_product(a, b) } -> Tensor2; - // divergence(a) - // rotation(a) -}; +concept VectorRepresentation = (!is_quantity) && Vector; -template -concept TensorRepresentation = Tensor && WeaklyRegular; // && requires(T a, T b) { - // TBD - // tensor operations - // { tensor_product(a, b) } -> Tensor4; - // { inner_product(a, b) } -> Tensor2; - // { scalar_product(a, b) } -> Scalar; -//}; +// template +// concept TensorRepresentation = (!is_quantity) && Tensor; + +template +concept IsOfCharacter = + (Ch == quantity_character::scalar && Scalar) || (Ch == quantity_character::complex && Complex) || + (Ch == quantity_character::vector && Vector); // || (Ch == quantity_character::tensor && Tensor); } // namespace detail MP_UNITS_EXPORT template concept Representation = detail::ScalarRepresentation || detail::ComplexRepresentation || - detail::VectorRepresentation || detail::TensorRepresentation; + detail::VectorRepresentation; // || detail::TensorRepresentation; MP_UNITS_EXPORT template concept RepresentationOf = diff --git a/src/core/include/mp-units/framework/symbol_text.h b/src/core/include/mp-units/framework/symbol_text.h index 71fc98b02..af3a84528 100644 --- a/src/core/include/mp-units/framework/symbol_text.h +++ b/src/core/include/mp-units/framework/symbol_text.h @@ -38,19 +38,8 @@ import std; #include // IWYU pragma: export #include #include -#endif -#endif - -#if __cpp_lib_text_encoding -#ifndef MP_UNITS_IN_MODULE_INTERFACE -#ifdef MP_UNITS_IMPORT_STD -import std; -#else -#include -#endif -#endif -static_assert(std::text_encoding::literal().mib() == std::text_encoding::id::UTF8); -#endif +#endif // MP_UNITS_IMPORT_STD +#endif // MP_UNITS_IN_MODULE_INTERFACE namespace mp_units { @@ -74,15 +63,22 @@ constexpr bool is_basic_literal_character_set_char(char ch) return ch == 0x00 || (0x07 <= ch && ch <= 0x0D) || (0x20 <= ch && ch <= 0x7E); }; +template +constexpr bool is_basic_literal_character_set(InputIt begin, InputIt end) noexcept +{ + return all_of(begin, end, is_basic_literal_character_set_char); +} + template constexpr bool is_basic_literal_character_set(const char (&txt)[N]) noexcept { - return detail::all_of(std::begin(txt), std::end(txt), is_basic_literal_character_set_char); + return is_basic_literal_character_set(std::begin(txt), std::end(txt)); } template constexpr fixed_u8string to_u8string(fixed_string txt) { + MP_UNITS_EXPECTS(is_basic_literal_character_set(txt.begin(), txt.end())); return std::bit_cast>(txt); } diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index ffea57200..132b5fd37 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -152,12 +152,6 @@ struct derived_unit; namespace detail { -template -struct unit_less : std::bool_constant() < type_name()> {}; - -template -using type_list_of_unit_less = expr_less; - struct unit_interface { /** * Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned. @@ -200,7 +194,7 @@ struct unit_interface { template [[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs rhs) { - return expr_multiply(lhs, rhs); + return expr_multiply(lhs, rhs); } /** @@ -211,7 +205,7 @@ struct unit_interface { template [[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs rhs) { - return expr_divide(lhs, rhs); + return expr_divide(lhs, rhs); } template @@ -621,7 +615,7 @@ template requires detail::non_zero [[nodiscard]] consteval Unit auto pow(U u) { - return detail::expr_pow(u); + return detail::expr_pow(u); } /** @@ -696,7 +690,7 @@ template else if constexpr (is_positive_integral_power(canonical_rhs.mag / canonical_lhs.mag)) return u1; else { - if constexpr (detail::unit_less::value) + if constexpr (detail::type_name_less::value) return common_unit{}; else return common_unit{}; @@ -730,7 +724,7 @@ struct collapse_common_unit_impl { template using collapse_common_unit = type_list_unique< - type_list_sort, NewUnit, false, Us...>::type, type_list_of_unit_less>>; + type_list_sort, NewUnit, false, Us...>::type, type_list_name_less>>; } // namespace detail diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index 490445a2f..8b9e63a70 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -55,7 +55,7 @@ namespace mp_units { */ template requires detail::non_zero && requires(Rep v) { - quantity_values::one(); + representation_values::one(); requires requires { pow(v, 1.0); } || requires { std::pow(v, 1.0); }; } [[nodiscard]] constexpr quantity(R), Rep> pow(const quantity& q) noexcept @@ -328,7 +328,7 @@ template requires((!treat_as_floating_point) || requires(Rep v) { floor(v); } || requires(Rep v) { std::floor(v); }) && (equivalent(To, get_unit(R)) || requires { q.force_in(To); - quantity_values::one(); + representation_values::one(); }) { const auto handle_signed_results = [&](const T& res) { @@ -365,7 +365,7 @@ template requires((!treat_as_floating_point) || requires(Rep v) { ceil(v); } || requires(Rep v) { std::ceil(v); }) && (equivalent(To, get_unit(R)) || requires { q.force_in(To); - quantity_values::one(); + representation_values::one(); }) { const auto handle_signed_results = [&](const T& res) { @@ -404,7 +404,7 @@ template requires((!treat_as_floating_point) || requires(Rep v) { round(v); } || requires(Rep v) { std::round(v); }) && (equivalent(To, get_unit(R)) || requires { ::mp_units::floor(q); - quantity_values::one(); + representation_values::one(); }) { if constexpr (equivalent(To, get_unit(R))) { @@ -438,11 +438,11 @@ template template [[nodiscard]] constexpr QuantityOf auto inverse(const quantity& q) requires requires { - quantity_values::one(); + representation_values::one(); value_cast(1 / q); } { - return (quantity_values::one() * one).force_in(To * quantity::unit) / q; + return (representation_values::one() * one).force_in(To * quantity::unit) / q; } /** diff --git a/test/runtime/linear_algebra_test.cpp b/test/runtime/linear_algebra_test.cpp index 85a7afc72..200bf644b 100644 --- a/test/runtime/linear_algebra_test.cpp +++ b/test/runtime/linear_algebra_test.cpp @@ -43,12 +43,6 @@ import mp_units; template using vector = STD_LA::fixed_size_column_vector; -template -constexpr bool mp_units::treat_as_floating_point> = mp_units::treat_as_floating_point; - -template -constexpr bool mp_units::is_vector> = true; - template std::ostream& operator<<(std::ostream& os, const vector& v) { @@ -87,8 +81,6 @@ template } template - requires is_vector && is_vector && - requires(typename Q1::rep v1, typename Q2::rep v2) { cross_product(v1, v2); } [[nodiscard]] constexpr QuantityOf auto cross_product(const Q1& q1, const Q2& q2) { return cross_product(q1.numerical_value_ref_in(q1.unit), q2.numerical_value_ref_in(q2.unit)) * @@ -299,10 +291,6 @@ TEST_CASE("vector quantity", "[la]") } } -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - TEST_CASE("vector of quantities", "[la]") { SECTION("cast of unit") diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index fbd20241a..d0d4552b0 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -278,13 +278,16 @@ static_assert(!Representation); // RepresentationOf static_assert(RepresentationOf); static_assert(!RepresentationOf); -static_assert(!RepresentationOf); +static_assert(RepresentationOf); static_assert(!RepresentationOf); static_assert(RepresentationOf); static_assert(!RepresentationOf); -static_assert(!RepresentationOf); +static_assert(RepresentationOf); static_assert(!RepresentationOf); static_assert(!RepresentationOf); +static_assert(!RepresentationOf); +static_assert(!RepresentationOf); +static_assert(!RepresentationOf); static_assert(!RepresentationOf, quantity_character::scalar>); #if MP_UNITS_HOSTED static_assert(RepresentationOf, quantity_character::complex>); diff --git a/test/static/custom_rep_test_min_impl.cpp b/test/static/custom_rep_test_min_impl.cpp index e2810eaac..a07524d04 100644 --- a/test/static/custom_rep_test_min_impl.cpp +++ b/test/static/custom_rep_test_min_impl.cpp @@ -60,9 +60,6 @@ class min_impl { } // namespace -template -constexpr bool mp_units::is_scalar> = true; - template struct std::common_type, min_impl> : std::type_identity>> {}; template diff --git a/test/static/hep_test.cpp b/test/static/hep_test.cpp index 62407bd40..623963f55 100644 --- a/test/static/hep_test.cpp +++ b/test/static/hep_test.cpp @@ -24,10 +24,6 @@ #include #include -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - namespace { using namespace mp_units; diff --git a/test/static/international_test.cpp b/test/static/international_test.cpp index 755eea73c..8d845f9a9 100644 --- a/test/static/international_test.cpp +++ b/test/static/international_test.cpp @@ -26,10 +26,6 @@ #include #include -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - namespace { using namespace mp_units; diff --git a/test/static/natural_test.cpp b/test/static/natural_test.cpp index 9986149a5..3281192fd 100644 --- a/test/static/natural_test.cpp +++ b/test/static/natural_test.cpp @@ -22,10 +22,6 @@ #include -template - requires mp_units::is_scalar -constexpr bool mp_units::is_vector = true; - namespace { using namespace mp_units; diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index a9a4a5e74..3b8d14d58 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -47,9 +47,6 @@ import std; #endif #endif -template<> -constexpr bool mp_units::is_vector = true; - namespace { using namespace mp_units; @@ -74,8 +71,10 @@ concept invalid_types = requires { requires !requires { typename Q; }; // quantity_spec instead of reference requires !requires { typename Q; }; // bool is not a valid representation type requires !requires { typename Q>; }; // quantity used as Rep - requires !requires { typename Q; }; // vector representation expected #if MP_UNITS_HOSTED + requires !requires { + typename Q>; + }; // vector representation expected requires !requires { typename Q>; }; // scalar representation expected