// 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, Scalar Rep> class quantity; template U, Scalar 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; } } } // namespace detail // QuantityOf template concept QuantityOf = Quantity && Dimension && equivalent_dim; // quantity_cast namespace detail { template struct quantity_cast_impl; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(q.count())); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * static_cast(detail::fpow10(CRatio.exp)))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(detail::ipow10(CRatio.exp)))); } else { return To(static_cast(static_cast(q.count()) / static_cast(detail::ipow10(-CRatio.exp)))); } } } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(static_cast(q.count()) * (static_cast(CRatio.num) / static_cast(CRatio.den)))); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * static_cast(detail::fpow10(CRatio.exp)) * (static_cast(CRatio.num) / static_cast(CRatio.den)))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(detail::ipow10(CRatio.exp)) / static_cast(CRatio.den))); } else { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / (static_cast(CRatio.den) * static_cast(detail::ipow10(-CRatio.exp))))); } } } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(static_cast(q.count()) / static_cast(CRatio.den))); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * static_cast(detail::fpow10(CRatio.exp)) * (CRep{1} / static_cast(CRatio.den)))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(detail::ipow10(CRatio.exp)) / static_cast(CRatio.den))); } else { return To(static_cast(static_cast(q.count()) / (static_cast(detail::ipow10(-CRatio.exp)) * static_cast(CRatio.den)))); } } } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num))); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(detail::fpow10(CRatio.exp)))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(detail::ipow10(CRatio.exp)))); } else { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / static_cast(detail::ipow10(-CRatio.exp)))); } } } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(q.count() * detail::fpow10(CRatio.exp))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(q.count() * detail::ipow10(CRatio.exp))); } else { return To(static_cast(q.count() / detail::ipow10(-CRatio.exp))); } } } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(q.count() * (CRatio.num / CRatio.den))); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(q.count() * detail::fpow10(CRatio.exp) * (CRatio.num / CRatio.den))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(q.count() * CRatio.num * detail::ipow10(CRatio.exp) / CRatio.den)); } else { return To(static_cast(q.count()) * CRatio.num / (CRatio.den * detail::ipow10(-CRatio.exp))); } } } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(q.count() / CRatio.den)); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(q.count() * detail::fpow10(CRatio.exp) / CRatio.den)); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(q.count() * detail::ipow10(CRatio.exp) / CRatio.den)); } else { return To(static_cast(q.count() / (detail::ipow10(-CRatio.exp) * CRatio.den))); } } } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(q.count() * CRatio.num)); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { return To(static_cast(q.count() * CRatio.num * detail::fpow10(CRatio.exp))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(q.count() * CRatio.num * detail::ipow10(CRatio.exp))); } else { return To(static_cast(q.count() * CRatio.num / detail::ipow10(-CRatio.exp))); } } } }; 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); } } } // namespace detail /** * @brief Explcit 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>(1q_ms); * * @tparam To a target quantity type to cast to */ template requires QuantityOf [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { using c_ratio = std::integral_constant(), To())>; using c_rep = std::common_type_t; using ret_unit = downcast_unit; using ret = quantity; using cast = detail::quantity_cast_impl; return cast::cast(q); } /** * @brief Explcit 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(200q_Gal); * * @tparam ToD a dimension type to use for a target quantity */ template requires equivalent_dim [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { return quantity_cast, Rep>>(q); } /** * @brief Explcit 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(1q_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 Explcit 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(1q_ms); * * @tparam ToRep a representation type to use for a target quantity */ template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { return quantity_cast>(q); } /** * @brief Explcit 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{1q_ms}); * auto q1 = units::quantity_point_cast>(quantity_point{1q_ms}); * auto q1 = units::quantity_point_cast(quantity_point{200q_Gal}); * auto q1 = units::quantity_point_cast(quantity_point{1q_ms}); * auto q1 = units::quantity_point_cast(quantity_point{1q_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())); } } // namespace units #ifdef _MSC_VER #pragma warning (pop) #endif //_MSC_VER