// The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #pragma once #include #include #include #include #include #include #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable:4244) // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int #endif //_MSC_VER namespace units { template U, QuantityValue Rep> class quantity; template U, QuantityValue Rep> class quantity_point; namespace detail { template constexpr auto quantity_ratio(const quantity&) { if constexpr(BaseDimension) { return U::ratio; } else { return D::base_units_ratio * U::ratio / D::coherent_unit::ratio; } } template constexpr ratio cast_ratio(const Q1& from, const Q2& to) { using FromU = TYPENAME Q1::unit; using ToU = TYPENAME Q2::unit; if constexpr(same_unit_reference::value) { return FromU::ratio / ToU::ratio; } else { return quantity_ratio(from) / quantity_ratio(to); } } 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 [[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(quantity(), To()); if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * (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.count()) * (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.count()) * 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> [[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 requires is_specialization_of || requires(quantity q) { quantity_cast(q); } [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& qp) { 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 [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point& q) { return quantity_point_cast>(q); } } // namespace units #ifdef _MSC_VER #pragma warning (pop) #endif //_MSC_VER