// 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 UNITS_DIAGNOSTIC_PUSH // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA namespace units { template U, Representation Rep> class quantity; template U, Representation Rep> class quantity_point; template U, Representation Rep> class quantity_kind; template U, Representation Rep> class quantity_point_kind; namespace detail { template inline constexpr ratio quantity_ratio = std::enable_if_t>{}; template inline constexpr ratio quantity_ratio> = [] { if constexpr (BaseDimension) { return U::ratio; } else { return D::base_units_ratio * U::ratio / D::coherent_unit::ratio; } }(); template inline constexpr ratio cast_ratio = [] { using FromU = TYPENAME QFrom::unit; using ToU = TYPENAME QTo::unit; if constexpr (same_unit_reference::value) { return FromU::ratio / ToU::ratio; } else { return quantity_ratio / quantity_ratio; } }(); 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> && 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 /** * @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 the target quantity type to cast to. For example: * * auto q1 = units::quantity_cast>(1_q_ms); * * @tparam To a target quantity type to cast to */ template Rep> requires QuantityOf && (std::constructible_from>) [[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; constexpr auto c_ratio = detail::cast_ratio, To>; if constexpr (treat_as_floating_point) { return To( static_cast(static_cast(q.number()) * (static_cast(c_ratio.num) * detail::fpow10(c_ratio.exp) / static_cast(c_ratio.den)))); } else { if constexpr (c_ratio.exp > 0) { return To(static_cast( static_cast(q.number()) * (static_cast(c_ratio.num) * static_cast(detail::ipow10(c_ratio.exp))) / static_cast(c_ratio.den))); } else { return To(static_cast( static_cast(q.number()) * static_cast(c_ratio.num) / (static_cast(c_ratio.den) * static_cast(detail::ipow10(-c_ratio.exp))))); } } } /** * @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); } /** * @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 both the target dimension and unit to cast to. For example: * * auto q1 = units::quantity_cast(v1); * * @note This cast is especially useful when working with quantities 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 [[nodiscard]] constexpr auto quantity_cast(const quantity& 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 representation to cast to. For example: * * auto q1 = units::quantity_cast(1_q_ms); * * @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) { return quantity_cast>(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 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 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 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); } } // namespace units UNITS_DIAGNOSTIC_POP