diff --git a/example/v2_framework.cpp b/example/v2_framework.cpp index 952f02bc..5328425e 100644 --- a/example/v2_framework.cpp +++ b/example/v2_framework.cpp @@ -23,6 +23,9 @@ #include +template +consteval bool print(); + template constexpr bool is_of_type(Expr) { @@ -34,33 +37,102 @@ namespace { using namespace units; using namespace units::si::unit_symbols; -constexpr auto power = 5 * si::power[W]; -static_assert(is_of_type{}, int>>(power)); +// clang-format off +inline constexpr struct activity_dim : decltype(1 / isq::time_dim) {} activity_dim; +inline constexpr struct activity : system_reference {} activity; +// clang-format on -constexpr auto speed = 5 * si::speed[m / s]; + +// Named quantity/dimension and unit static_assert( - is_of_type>>{}, int>>( - speed)); + is_same_v{}, int>>); -constexpr auto q = 10 * si::length[m] / (2 * si::time[s]); -static_assert(is_of_type>, - derived_unit>>{}, - int>>(q)); +// Named quantity/dimension and derived (unnamed) unit +static_assert( + is_same_v>>{}, int>>); -constexpr auto distance = 5 * si::speed[m / s] * (5 * si::time[s]); +// Derived (unnamed) quantity/dimension and derived (unnamed) unit +static_assert(is_same_v>, + derived_unit>>{}, + int>>); -static_assert(is_of_type{}, int>>(distance)); +// Base quantity as a result of dimensional transformation +static_assert(is_same_v{}, int>>); -constexpr auto dimensionless = 20 * si::speed[m / s] / (10 * si::length[m]) * (5 * si::time[s]); +// Dimensionless +static_assert(is_same_v{}, int>>); -static_assert(is_of_type{}, int>>(dimensionless)); +// Comparisons +// Named and derived dimensions (same units) +static_assert(10 * si::length[m] / (2 * si::time[s]) == 5 * si::speed[m / s]); +static_assert(5 * si::speed[m / s] == 10 * si::length[m] / (2 * si::time[s])); -// constexpr auto q1 = 10 * si::length[m] / (2 * si::time[s]) + 5 * si::speed[m / s]; -// static_assert(is_of_type>, -// derived_unit>>{}, -// int>>(q1)); +// Named and derived dimensions (different units) +static_assert(10 / (2 * si::time[s]) == 5 * si::frequency[Hz]); +static_assert(5 * si::frequency[Hz] == 10 / (2 * si::time[s])); +// Different named dimensions +template +concept invalid_comparison = requires { + requires !requires { 2 * R1 == 2 * R2; }; + requires !requires { 2 * R2 == 2 * R1; }; + }; +static_assert(invalid_comparison); + +// static_assert(print()); + +// Arithmetics + +// Named and derived dimensions (same units) +static_assert(10 * si::length[m] / (2 * si::time[s]) + 5 * si::speed[m / s] == 10 * si::speed[m / s]); +static_assert(5 * si::speed[m / s] + 10 * si::length[m] / (2 * si::time[s]) == 10 * si::speed[m / s]); +static_assert(10 * si::length[m] / (2 * si::time[s]) - 5 * si::speed[m / s] == 0 * si::speed[m / s]); +static_assert(5 * si::speed[m / s] - 10 * si::length[m] / (2 * si::time[s]) == 0 * si::speed[m / s]); +static_assert( + is_same_v>>{}, int>>); +static_assert( + is_same_v>>{}, int>>); +static_assert( + is_same_v>>{}, int>>); +static_assert( + is_same_v>>{}, int>>); + +// Named and derived dimensions (different units) +static_assert(10 / (2 * si::time[s]) + 5 * si::frequency[Hz] == 10 * si::frequency[Hz]); +static_assert(5 * si::frequency[Hz] + 10 / (2 * si::time[s]) == 10 * si::frequency[Hz]); +static_assert(10 / (2 * si::time[s]) - 5 * si::frequency[Hz] == 0 * si::frequency[Hz]); +static_assert(5 * si::frequency[Hz] - 10 / (2 * si::time[s]) == 0 * si::frequency[Hz]); +static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); +static_assert(is_same_v{}, int>>); + +// Different named dimensions +template +consteval bool invalid_arithmetic(Ts... ts) +{ + return requires { + requires !requires { (... + ts); }; + requires !requires { (... - ts); }; + }; +} +static_assert(invalid_arithmetic(5 * activity[Bq], 5 * si::frequency[Hz])); +static_assert(invalid_arithmetic(5 * activity[Bq], 10 / (2 * si::time[s]), 5 * si::frequency[Hz])); + +// static_assert(quantity_of); // static_assert(quantity_of); // static_assert(quantity_of); // static_assert(!quantity_of); @@ -222,8 +294,6 @@ namespace units::isq::si { // quantity speed3(20); // quantity speed4(20); -template -void print(); // constexpr auto avg_speed(quantity d, quantity t) { return d / t; } diff --git a/src/core/include/units/bits/common_type.h b/src/core/include/units/bits/common_type.h deleted file mode 100644 index 05288572..00000000 --- a/src/core/include/units/bits/common_type.h +++ /dev/null @@ -1,122 +0,0 @@ -// 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 units { - -// template U, Representation Rep> -// class quantity_point; - -// template U, Representation Rep> -// class quantity_kind; - -// template U, Representation Rep> -// class quantity_point_kind; - -// TODO common_unit should use common_magnitude(U1::mag, U2::mag) - -template - requires(equivalent(D1{}, D2{})) -using common_dimension = conditional, D1, detail::dim_type>; - -template -// requires(equivalent) -using common_unit = U1; - -namespace detail { - -template -struct common_quantity_reference_impl; - -template -struct common_quantity_reference_impl { - using type = R; -}; - -template -struct common_quantity_reference_impl, reference> { - using type = reference>; -}; - -template - requires(equivalent(R1::dimension, R2::dimension)) -struct common_quantity_reference_impl { - using type = reference, - common_unit>; -}; - -template Q2> -using common_quantity_reference = - TYPENAME detail::common_quantity_reference_impl, - std::remove_const_t>::type; - -} // namespace detail -} // namespace units - -namespace std { - -template Q2> - requires requires { typename common_type_t; } -struct common_type { -private: - using ref = units::detail::common_quantity_reference; -public: - using type = units::quantity>; -}; - -// template QP2> -// requires requires { typename common_type_t; } -// struct common_type { -// using type = -// units::quantity_point::dimension>, -// typename common_type_t::unit, -// typename common_type_t::rep>; -// }; - -// template QK2> -// requires requires { typename common_type_t; } -// struct common_type { -// using type = -// units::quantity_kind::unit, -// typename common_type_t::rep>; -// }; - -// template QPK2> -// requires requires { typename common_type_t; } -// struct common_type { -// using type = units::quantity_point_kind< -// typename QPK1::point_kind_type, -// typename common_type_t::unit, -// typename common_type_t::rep>; -// }; - -} // namespace std diff --git a/src/core/include/units/concepts.h b/src/core/include/units/concepts.h index c506c2ac..32a91ea6 100644 --- a/src/core/include/units/concepts.h +++ b/src/core/include/units/concepts.h @@ -102,15 +102,7 @@ inline constexpr bool is_quantity> = true; */ template concept quantity_of = Quantity && ((Dimension> && Q::dimension == V) || - (Reference> && Q::dimension == V.dimension)); - -/** - * @brief A concept matching two equivalent quantities - * - * Satisfied by quantities having equivalent dimensions and units. - */ -template -concept quantity_equivalent_to = Quantity && equivalent(Q1::dimension, Q2::dimension) && - equivalent(Q1::unit, Q2::unit); + (Reference> && Q::dimension == V.dimension && + Q::unit == V.unit)); } // namespace units diff --git a/src/core/include/units/dimension.h b/src/core/include/units/dimension.h index fd3740a5..d8d5f7b6 100644 --- a/src/core/include/units/dimension.h +++ b/src/core/include/units/dimension.h @@ -195,6 +195,23 @@ template return is_same_v, detail::dim_type>; } +template +[[nodiscard]] consteval bool convertible(D1, D2) +{ + return std::derived_from || std::derived_from; +} + // TODO consider adding the support for text output of the dimensional equation } // namespace units + +namespace std { + +template + requires(units::convertible(D1{}, D2{})) +struct common_type { + using type = ::units::conditional, std::remove_const_t>, + std::remove_const_t, std::remove_const_t>; +}; + +} // namespace std diff --git a/src/core/include/units/quantity.h b/src/core/include/units/quantity.h index 7507359e..39cbf51b 100644 --- a/src/core/include/units/quantity.h +++ b/src/core/include/units/quantity.h @@ -23,13 +23,9 @@ #pragma once -// #include -// #include - // IWYU pragma: begin_exports -// #include +#include // #include -#include #include #include #include @@ -38,8 +34,7 @@ #include // IWYU pragma: end_exports -// #include -// #include +#include namespace units { @@ -73,8 +68,9 @@ concept harmonic_ = // exposition only // Quantity && Quantity && is_integral(detail::quantity_magnitude / detail::quantity_magnitude); template -concept safe_castable_to_ = // exposition only - Quantity && quantity_of && scalable_with_ && +concept quantity_convertible_to_ = // exposition only + Quantity && Quantity && convertible(QFrom::dimension, QTo::dimension) && + convertible(QFrom::unit, QTo::unit) && scalable_with_ && (floating_point_ || (!floating_point_ && harmonic_)); template @@ -161,13 +157,13 @@ public: { } - template Q> + template Q> constexpr explicit(false) quantity(const Q& q) : number_(quantity_cast(q).number()) { } template - requires(safe_castable_to_, quantity>) + requires(quantity_convertible_to_, quantity>) constexpr explicit quantity(const Q& q) : quantity(quantity_like_type(quantity_like_traits::number(q))) { } @@ -423,7 +419,7 @@ public: requires(!Quantity) && (invoke_result_convertible_to_, const Value&, rep>) [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) { - return detail::make_quantity(v / q.number()); + return detail::make_quantity(v / q.number()); } template @@ -475,20 +471,20 @@ explicit quantity(Q) typename quantity_like_traits::rep>; // non-member binary operators -template Q2> +template Q2> requires(quantity_value_for_, typename Q1::rep, typename Q2::rep>) [[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs) { - using ref = detail::common_quantity_reference; + using ref = std::common_type_t; using ret = quantity; return ret(ret(lhs).number() + ret(rhs).number()); } -template Q2> +template Q2> requires(quantity_value_for_, typename Q1::rep, typename Q2::rep>) [[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs) { - using ref = detail::common_quantity_reference; + using ref = std::common_type_t; using ret = quantity; return ret(ret(lhs).number() - ret(rhs).number()); } @@ -510,7 +506,7 @@ template template requires(!floating_point_) && (!floating_point_) && - (quantity_equivalent_to || quantity_of) && + (std::convertible_to || quantity_of) && (quantity_value_for_, typename Q1::rep, typename Q2::rep>) [[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs) { @@ -520,20 +516,36 @@ template return ret(lhs.number() % rhs.number()); } -template Q2> +template Q2> requires std::three_way_comparable_with [[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs) { - using cq = std::common_type_t; - return cq(lhs).number() <=> cq(rhs).number(); + using ref = std::common_type_t; + return quantity_cast(lhs).number() <=> quantity_cast(rhs).number(); } -template Q2> +template Q2> requires std::equality_comparable_with [[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs) { - using cq = std::common_type_t; - return cq(lhs).number() == cq(rhs).number(); + using ref = std::common_type_t; + return quantity_cast(lhs).number() == quantity_cast(rhs).number(); } } // namespace units + +namespace std { + +template + requires requires { + typename common_type_t, remove_const_t>; + typename common_type_t; + } +struct common_type { +private: + using ref = common_type_t, remove_const_t>; +public: + using type = units::quantity>; +}; + +} // namespace std diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index 4449d722..bf4da51b 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -22,12 +22,13 @@ #pragma once -#include #include #include #include +#include #include #include +#include UNITS_DIAGNOSTIC_PUSH // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int @@ -35,53 +36,53 @@ UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA namespace units { -template U, Representation Rep> +template class quantity; -template U, Representation Rep> -class quantity_point; +// template U, Representation Rep> +// class quantity_point; -template U, Representation Rep> -class quantity_kind; +// template U, Representation Rep> +// class quantity_kind; -template U, Representation Rep> -class quantity_point_kind; +// template U, Representation Rep> +// class quantity_point_kind; namespace detail { -template -inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag; +// template +// inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag; -template -inline constexpr Magnitude auto cast_magnitude = [] { - using FromU = TYPENAME QFrom::unit; - using ToU = TYPENAME QTo::unit; - if constexpr (same_unit_reference::value) { - return FromU::mag / ToU::mag; - } else { - return quantity_magnitude / quantity_magnitude; - } -}(); +// template +// inline constexpr Magnitude auto cast_magnitude = [] { +// using FromU = TYPENAME QFrom::unit; +// using ToU = TYPENAME QTo::unit; +// if constexpr (same_unit_reference::value) { +// return FromU::mag / ToU::mag; +// } else { +// return quantity_magnitude / quantity_magnitude; +// } +// }(); -template -struct cast_traits; +// template +// struct cast_traits; -template - requires common_type_with_, std::intmax_t> -struct cast_traits { - using ratio_type = std::common_type_t, std::intmax_t>; - using rep_type = ratio_type; -}; +// template +// requires common_type_with_, std::intmax_t> +// struct cast_traits { +// using ratio_type = std::common_type_t, std::intmax_t>; +// using rep_type = ratio_type; +// }; -template - requires(!common_type_with_, std::intmax_t> && - scalable_number_, std::intmax_t> && - requires { typename std::common_type_t::value_type; } && - common_type_with_::value_type, std::intmax_t>) -struct cast_traits { - using ratio_type = std::common_type_t::value_type, std::intmax_t>; - using rep_type = std::common_type_t; -}; +// template +// requires(!common_type_with_, std::intmax_t> && +// scalable_number_, std::intmax_t> && +// requires { typename std::common_type_t::value_type; } && +// common_type_with_::value_type, std::intmax_t>) +// struct cast_traits { +// using ratio_type = std::common_type_t::value_type, std::intmax_t>; +// using rep_type = std::common_type_t; +// }; } // namespace detail @@ -97,59 +98,28 @@ struct cast_traits { * * @tparam To a target quantity type to cast to */ -template Rep> - requires QuantityOf && (std::constructible_from>) -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) +template Rep> + requires(convertible(R, To::reference)) +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - using traits = detail::cast_traits; - using ratio_type = TYPENAME traits::ratio_type; - using rep_type = TYPENAME traits::rep_type; + // TODO implement same unit magnitude check + if constexpr (std::same_as) { + return To(static_cast(q.number())); + } else { + // using traits = detail::cast_traits; + // using ratio_type = TYPENAME traits::ratio_type; + // using rep_type = TYPENAME traits::rep_type; - constexpr Magnitude auto c_mag = detail::cast_magnitude, To>; - constexpr Magnitude auto num = numerator(c_mag); - constexpr Magnitude auto den = denominator(c_mag); - constexpr Magnitude auto irr = c_mag * (den / num); + // constexpr Magnitude auto c_mag = detail::cast_magnitude, To>; + // constexpr Magnitude auto num = numerator(c_mag); + // constexpr Magnitude auto den = denominator(c_mag); + // constexpr Magnitude auto irr = c_mag * (den / num); - constexpr auto val = [](Magnitude auto m) { return get_value(m); }; - return To(static_cast(static_cast(q.number()) * val(num) / val(den) * val(irr))); -} - -/** - * @brief Explicit cast of a quantity - * - * Implicit conversions between quantities of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * - * This cast gets only the target dimension to cast to. For example: - * - * auto q1 = units::quantity_cast(200_q_Gal); - * - * @tparam ToD a dimension type to use for a target quantity - */ -template - requires equivalent -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) -{ - return quantity_cast, Rep>>(q); -} - -/** - * @brief Explicit cast of a quantity - * - * Implicit conversions between quantities of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * - * This cast gets only the target unit to cast to. For example: - * - * auto q1 = units::quantity_cast(1_q_ms); - * - * @tparam ToU a unit type to use for a target quantity - */ -template - requires UnitOf -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) -{ - return quantity_cast>(q); + // constexpr auto val = [](Magnitude auto m) { return get_value(m); }; + // return To(static_cast(static_cast(q.number()) * val(num) / val(den) * val(irr))); + // TODO implement that + return q; + } } /** @@ -168,11 +138,52 @@ template * @tparam ToD a dimension type to use for a target quantity * @tparam ToU a unit type to use for a target quantity */ -template - requires equivalent && UnitOf -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) +template + requires(convertible(ToR, R)) +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - return quantity_cast>(q); + return quantity_cast>(q); +} + + +/** + * @brief Explicit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only the target dimension to cast to. For example: + * + * auto q1 = units::quantity_cast(200_q_Gal); + * + * @tparam ToD a dimension type to use for a target quantity + */ +template + requires(convertible(ToD, R.dimension)) +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) +{ + constexpr reference, std::remove_const_t> r; + return quantity_cast>(q); +} + +/** + * @brief Explicit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only the target unit to cast to. For example: + * + * auto q1 = units::quantity_cast(1_q_ms); + * + * @tparam ToU a unit type to use for a target quantity + */ +template + requires(convertible(ToU, R.unit)) +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) +{ + constexpr reference, std::remove_const_t> r; + return quantity_cast>(q); } /** @@ -187,185 +198,187 @@ template * * @tparam ToRep a representation type to use for a target quantity */ -template Rep> - requires(std::constructible_from>) -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) +template Rep> +// requires(std::constructible_from>) +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - return quantity_cast>(q); + return To(static_cast(q.number())); } -/** - * @brief Explicit cast of a quantity point - * - * Implicit conversions between quantity points of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. - * - * This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example: - * - * auto q1 = units::quantity_point_cast(quantity_point{1_q_ms}); - * auto q1 = units::quantity_point_cast>(quantity_point{1_q_ms}); - * auto q1 = units::quantity_point_cast(quantity_point{200_q_Gal}); - * auto q1 = units::quantity_point_cast(quantity_point{1_q_ms}); - * auto q1 = units::quantity_point_cast(quantity_point{1_q_ms}); - * - * @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast - */ -template -[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& qp) - requires requires { - requires is_specialization_of; - requires requires { quantity_cast(qp.relative()); }; - requires equivalent; - } || // TODO: Simplify when Clang catches up. - requires { quantity_cast(qp.relative()); } -{ - if constexpr (is_specialization_of) - return quantity_point(quantity_cast(qp.relative())); - else - return quantity_point(quantity_cast(qp.relative())); -} +// /** +// * @brief Explicit cast of a quantity point +// * +// * Implicit conversions between quantity points of different types are allowed only for "safe" +// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. +// * +// * This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example: +// * +// * auto q1 = units::quantity_point_cast(quantity_point{1_q_ms}); +// * auto q1 = units::quantity_point_cast>(quantity_point{1_q_ms}); +// * auto q1 = units::quantity_point_cast(quantity_point{200_q_Gal}); +// * auto q1 = units::quantity_point_cast(quantity_point{1_q_ms}); +// * auto q1 = units::quantity_point_cast(quantity_point{1_q_ms}); +// * +// * @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast +// */ +// template +// [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& qp) +// requires requires { +// requires is_specialization_of; +// requires requires { quantity_cast(qp.relative()); }; +// requires equivalent; +// } || // TODO: Simplify when Clang catches up. +// requires { quantity_cast(qp.relative()); } +// { +// if constexpr (is_specialization_of) +// return quantity_point(quantity_cast(qp.relative())); +// else +// return quantity_point(quantity_cast(qp.relative())); +// } -/** - * @brief Explicit cast of a quantity point - * - * Implicit conversions between quantity points of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. - * - * This cast gets both the target dimension and unit to cast to. For example: - * - * auto q1 = units::quantity_point_cast(v1); - * - * @note This cast is especially useful when working with quantity points of unknown dimensions - * (@c unknown_dimension). - * - * @tparam ToD a dimension type to use for a target quantity - * @tparam ToU a unit type to use for a target quantity - */ -template - requires equivalent && UnitOf && RebindablePointOriginFor -[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& q) -{ - return quantity_point_cast, ToU, Rep>>(q); -} +// /** +// * @brief Explicit cast of a quantity point +// * +// * Implicit conversions between quantity points of different types are allowed only for "safe" +// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. +// * +// * This cast gets both the target dimension and unit to cast to. For example: +// * +// * auto q1 = units::quantity_point_cast(v1); +// * +// * @note This cast is especially useful when working with quantity points of unknown dimensions +// * (@c unknown_dimension). +// * +// * @tparam ToD a dimension type to use for a target quantity +// * @tparam ToU a unit type to use for a target quantity +// */ +// template +// requires equivalent && UnitOf && RebindablePointOriginFor +// [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& q) +// { +// return quantity_point_cast, ToU, Rep>>(q); +// } -/** - * @brief Explicit cast of a quantity kind - * - * Implicit conversions between quantity kinds of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. - * - * This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example: - * - * auto q1 = units::quantity_kind_cast(quantity_kind{ns::width{1 * mm}); - * auto q1 = units::quantity_kind_cast(ns::width{1 * m}); - * auto q1 = units::quantity_kind_cast>(ns::width{1 * mm}); - * auto q1 = units::quantity_kind_cast(ns::rate_of_climb{200 * Gal}); - * auto q1 = units::quantity_kind_cast(ns::width{1 * mm}); - * auto q1 = units::quantity_kind_cast(ns::width{1.0 * mm}); - * - * @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast - */ -template -[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind& qk) - requires requires { - requires is_specialization_of; - requires requires { quantity_cast(qk.common()); }; - } || requires { - requires Kind; - requires UnitOf; - } || requires { quantity_cast(qk.common()); } // TODO: Simplify when Clang catches up. -{ - if constexpr (is_specialization_of) - return CastSpec(quantity_cast(qk.common())); - else if constexpr (Kind) - return quantity_kind(qk.common()); - else { - auto q{quantity_cast(qk.common())}; - using Q = decltype(q); - return quantity_kind(static_cast(q)); - } -} +// /** +// * @brief Explicit cast of a quantity kind +// * +// * Implicit conversions between quantity kinds of different types are allowed only for "safe" +// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. +// * +// * This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example: +// * +// * auto q1 = units::quantity_kind_cast(quantity_kind{ns::width{1 * mm}); +// * auto q1 = units::quantity_kind_cast(ns::width{1 * m}); +// * auto q1 = units::quantity_kind_cast>(ns::width{1 * mm}); +// * auto q1 = units::quantity_kind_cast(ns::rate_of_climb{200 * Gal}); +// * auto q1 = units::quantity_kind_cast(ns::width{1 * mm}); +// * auto q1 = units::quantity_kind_cast(ns::width{1.0 * mm}); +// * +// * @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast +// */ +// template +// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind& qk) +// requires requires { +// requires is_specialization_of; +// requires requires { quantity_cast(qk.common()); }; +// } || requires { +// requires Kind; +// requires UnitOf; +// } || requires { quantity_cast(qk.common()); } // TODO: Simplify when Clang catches up. +// { +// if constexpr (is_specialization_of) +// return CastSpec(quantity_cast(qk.common())); +// else if constexpr (Kind) +// return quantity_kind(qk.common()); +// else { +// auto q{quantity_cast(qk.common())}; +// using Q = decltype(q); +// return quantity_kind(static_cast(q)); +// } +// } -/** - * @brief Explicit cast of a quantity kind - * - * Implicit conversions between quantity kinds of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. - * - * This cast gets both the target kind and unit to cast to. For example: - * - * auto q1 = units::quantity_kind_cast(w); - * - * @note This cast is especially useful when working with quantity kinds of unknown kind. - * - * @tparam ToK the kind type to use for the target quantity - * @tparam ToU the unit type to use for the target quantity - */ -template - requires equivalent && UnitOf -[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind& qk) -{ - return quantity_kind_cast>(qk); -} +// /** +// * @brief Explicit cast of a quantity kind +// * +// * Implicit conversions between quantity kinds of different types are allowed only for "safe" +// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. +// * +// * This cast gets both the target kind and unit to cast to. For example: +// * +// * auto q1 = units::quantity_kind_cast(w); +// * +// * @note This cast is especially useful when working with quantity kinds of unknown kind. +// * +// * @tparam ToK the kind type to use for the target quantity +// * @tparam ToU the unit type to use for the target quantity +// */ +// template +// requires equivalent && UnitOf +// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind& qk) +// { +// return quantity_kind_cast>(qk); +// } -/** - * @brief Explicit cast of a quantity point kind - * - * Implicit conversions between quantity point kinds of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. - * - * This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For - * example: - * - * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); - * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); - * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * m}); - * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * m}); - * auto q1 = units::quantity_point_kind_cast>(ns::x_coordinate{1 * mm}); - * auto q1 = units::quantity_point_kind_cast(quantity_point_kind(ns::rate_of_climb{200 - * * Gal})); auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); auto q1 = - * units::quantity_point_kind_cast(ns::x_coordinate{1.0 * mm}); - * - * @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast - */ -template -[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind& qpk) - requires requires { - requires is_specialization_of; - requires requires { quantity_kind_cast(qpk.relative()); }; - requires equivalent; - } || requires { requires PointKind && UnitOf; } || - requires { quantity_kind_cast(qpk.relative()); } // TODO: Simplify when Clang catches up. -{ - if constexpr (is_specialization_of) - return CastSpec(quantity_kind_cast(qpk.relative())); - else if constexpr (PointKind) - return quantity_point_kind(quantity_kind_cast(qpk.relative())); - else - return quantity_point_kind(quantity_kind_cast(qpk.relative())); -} +// /** +// * @brief Explicit cast of a quantity point kind +// * +// * Implicit conversions between quantity point kinds of different types are allowed only for "safe" +// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. +// * +// * This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For +// * example: +// * +// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); +// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); +// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * m}); +// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * m}); +// * auto q1 = units::quantity_point_kind_cast>(ns::x_coordinate{1 * +// mm}); +// * auto q1 = +// units::quantity_point_kind_cast(quantity_point_kind(ns::rate_of_climb{200 +// * * Gal})); auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); auto q1 = +// * units::quantity_point_kind_cast(ns::x_coordinate{1.0 * mm}); +// * +// * @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast +// */ +// template +// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind& qpk) +// requires requires { +// requires is_specialization_of; +// requires requires { quantity_kind_cast(qpk.relative()); }; +// requires equivalent; +// } || requires { requires PointKind && UnitOf; } || +// requires { quantity_kind_cast(qpk.relative()); } // TODO: Simplify when Clang catches up. +// { +// if constexpr (is_specialization_of) +// return CastSpec(quantity_kind_cast(qpk.relative())); +// else if constexpr (PointKind) +// return quantity_point_kind(quantity_kind_cast(qpk.relative())); +// else +// return quantity_point_kind(quantity_kind_cast(qpk.relative())); +// } -/** - * @brief Explicit cast of a quantity point kind - * - * Implicit conversions between quantity point kinds of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. - * - * This cast gets both the target point kind and unit to cast to. For example: - * - * auto q1 = units::quantity_point_kind_cast(x); - * - * @note This cast is especially useful when working with quantity point kinds of unknown point kind. - * - * @tparam ToPK the point kind type to use for the target quantity - * @tparam ToU the unit type to use for the target quantity - */ -template - requires equivalent && UnitOf -[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind& qpk) -{ - return quantity_point_kind_cast>(qpk); -} +// /** +// * @brief Explicit cast of a quantity point kind +// * +// * Implicit conversions between quantity point kinds of different types are allowed only for "safe" +// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. +// * +// * This cast gets both the target point kind and unit to cast to. For example: +// * +// * auto q1 = units::quantity_point_kind_cast(x); +// * +// * @note This cast is especially useful when working with quantity point kinds of unknown point kind. +// * +// * @tparam ToPK the point kind type to use for the target quantity +// * @tparam ToU the unit type to use for the target quantity +// */ +// template +// requires equivalent && UnitOf +// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind& qpk) +// { +// return quantity_point_kind_cast>(qpk); +// } } // namespace units diff --git a/src/core/include/units/reference.h b/src/core/include/units/reference.h index 62e151de..30dd479a 100644 --- a/src/core/include/units/reference.h +++ b/src/core/include/units/reference.h @@ -136,13 +136,27 @@ template void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete; +template +[[nodiscard]] consteval bool equivalent(R1, R2) +{ + return equivalent(R1::dimension, R2::dimension) && equivalent(R1::unit, R2::unit); +} + +template +[[nodiscard]] consteval bool convertible(R1, R2) +{ + return convertible(R1::dimension, R2::dimension) && convertible(R1::unit, R2::unit); +} + + template struct system_reference { static constexpr auto dimension = Dim; static constexpr auto coherent_unit = CoU; template - // requires same_unit_reference + // TODO enable that + // requires(convertible(coherent_unit, U{})) [[nodiscard]] constexpr reference, U> operator[](U) const { return {}; @@ -153,3 +167,20 @@ inline constexpr struct dimensionless : system_reference { } dimensionless; } // namespace units + +namespace std { + +template + requires requires { + typename common_type_t, remove_const_t>; + typename common_type_t, remove_const_t>; + } +struct common_type { +private: + using dim = common_type_t, remove_const_t>; + using unit = common_type_t, remove_const_t>; +public: + using type = units::reference; +}; + +} // namespace std diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index 0646e2fd..a7b4af77 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -330,12 +330,6 @@ template return is_same_v; } -template -[[nodiscard]] consteval bool equivalent(U1, U2) -{ - return true; // TODO implement this -} - // template // constexpr bool operator==(D1, D2) @@ -366,6 +360,20 @@ template // std::is_same_v; // } +// TODO implement this +// template +// [[nodiscard]] consteval bool equivalent(D1, D2) +// { +// return is_same_v, detail::dim_type>; +// } + +template +[[nodiscard]] consteval bool convertible(U1, U2) +{ + // TODO implement this + return std::derived_from || std::derived_from; +} + template struct square_ : decltype(U{} * U{}) {}; @@ -379,3 +387,15 @@ template inline constexpr cubic_> cubic; } // namespace units + +namespace std { + +// TODO implement this +template + requires(units::convertible(U1{}, U2{})) +struct common_type { + using type = ::units::conditional, std::remove_const_t>, + std::remove_const_t, std::remove_const_t>; +}; + +} // namespace std diff --git a/test/unit_test/static/dimension_test.cpp b/test/unit_test/static/dimension_test.cpp index 4cc2958e..d3dde201 100644 --- a/test/unit_test/static/dimension_test.cpp +++ b/test/unit_test/static/dimension_test.cpp @@ -36,14 +36,14 @@ inline constexpr struct length_dim_ : base_dimension<"L"> {} length_dim; inline constexpr struct time_dim_ : base_dimension<"T"> {} time_dim; inline constexpr struct frequency_dim_ : decltype(1 / time_dim) {} frequency_dim; +inline constexpr struct action_dim_ : decltype(1 / time_dim) {} action_dim; inline constexpr struct area_dim_ : decltype(length_dim * length_dim) {} area_dim; inline constexpr struct volume_dim_ : decltype(area_dim * length_dim) {} volume_dim; inline constexpr struct speed_dim_ : decltype(length_dim / time_dim) {} speed_dim; +inline constexpr struct velocity_dim_ : speed_dim_ {} velocity_dim; inline constexpr struct acceleration_dim_ : decltype(speed_dim / time_dim) {} acceleration_dim; // clang-format on -} // namespace - // concepts verification static_assert(BaseDimension); static_assert(!BaseDimension); @@ -107,13 +107,19 @@ static_assert(length_dim / length_dim == one_dim); static_assert(1 / time_dim != frequency_dim); static_assert(equivalent(1 / time_dim, frequency_dim)); +static_assert(convertible(1 / time_dim, frequency_dim)); static_assert(1 / frequency_dim == time_dim); static_assert(frequency_dim * time_dim == one_dim); +static_assert(std::is_same_v, frequency_dim_>); +static_assert(std::is_same_v, frequency_dim_>); static_assert(length_dim * length_dim != area_dim); static_assert(equivalent(length_dim * length_dim, area_dim)); +static_assert(convertible(length_dim * length_dim, area_dim)); static_assert(length_dim * length_dim != volume_dim); static_assert(area_dim / length_dim == length_dim); +static_assert(std::is_same_v, area_dim_>); +static_assert(std::is_same_v, area_dim_>); static_assert(length_dim * length_dim * length_dim != volume_dim); static_assert(equivalent(length_dim * length_dim * length_dim, volume_dim)); @@ -134,6 +140,10 @@ static_assert(length_dim * time_dim != speed_dim); static_assert(length_dim / time_dim / time_dim != speed_dim); static_assert(length_dim / speed_dim == time_dim); static_assert(speed_dim * time_dim == length_dim); +static_assert(std::is_same_v, speed_dim_>); +static_assert(std::is_same_v, speed_dim_>); +static_assert(std::is_same_v, + decltype(length_dim / time_dim)>); static_assert(length_dim / time_dim / time_dim != acceleration_dim); static_assert(equivalent(length_dim / time_dim / time_dim, acceleration_dim)); @@ -147,3 +157,21 @@ static_assert(equivalent(acceleration_dim * time_dim, speed_dim)); static_assert(acceleration_dim * (time_dim * time_dim) == length_dim); static_assert(acceleration_dim / speed_dim != frequency_dim); static_assert(equivalent(acceleration_dim / speed_dim, frequency_dim)); + +static_assert(frequency_dim != action_dim); +static_assert(equivalent(frequency_dim, action_dim)); +static_assert(!convertible(frequency_dim, action_dim)); +template +concept no_common_type = requires { + requires !requires { typename std::common_type_t; }; + requires !requires { typename std::common_type_t; }; + }; +static_assert(no_common_type); + +static_assert(velocity_dim != speed_dim); +static_assert(equivalent(velocity_dim, speed_dim)); +static_assert(convertible(speed_dim, velocity_dim)); +static_assert(std::is_same_v, velocity_dim_>); +static_assert(std::is_same_v, velocity_dim_>); + +} // namespace