// 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 namespace units { constexpr std::intmax_t ipow10(std::intmax_t exp) { assert(exp >= 0); if (exp == 0) return 1; std::intmax_t result = 1; while (exp > 0) { result *= 10; --exp; } return result; } constexpr long double fpow10(std::intmax_t exp) { if (exp == 0) return 1.0L; long double result = 1.0L; if (exp < 0) { while (exp < 0) { result /= 10.0L; ++exp; } } else { while (exp > 0) { result *= 10.0L; --exp; } } return result; } // 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(fpow10(CRatio::exp)))); } else { if constexpr (CRatio::exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio::exp)))); } else { return To(static_cast(static_cast(q.count()) / static_cast(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(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(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(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(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(ipow10(CRatio::exp)) / static_cast(CRatio::den))); } else { return To(static_cast(static_cast(q.count()) / (static_cast(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(fpow10(CRatio::exp)))); } else { if constexpr (CRatio::exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) * static_cast(ipow10(CRatio::exp)))); } else { return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) / static_cast(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() * fpow10(CRatio::exp))); } else { if constexpr (CRatio::exp > 0) { return To(static_cast(q.count() * ipow10(CRatio::exp))); } else { return To(static_cast(q.count() / 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() * fpow10(CRatio::exp) * (CRatio::num / CRatio::den))); } else { if constexpr (CRatio::exp > 0) { return To(static_cast(q.count() * CRatio::num * ipow10(CRatio::exp) / CRatio::den)); } else { return To(static_cast(q.count()) * CRatio::num / (CRatio::den * 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() * fpow10(CRatio::exp) / CRatio::den)); } else { if constexpr (CRatio::exp > 0) { return To(static_cast(q.count() * ipow10(CRatio::exp) / CRatio::den)); } else { return To(static_cast(q.count() / (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 * fpow10(CRatio::exp))); } else { if constexpr (CRatio::exp > 0) { return To(static_cast(q.count() * CRatio::num * ipow10(CRatio::exp))); } else { return To(static_cast(q.count() * CRatio::num / ipow10(-CRatio::exp))); } } } }; template struct cast_ratio; template struct cast_ratio { using type = ratio_divide; }; template requires same_unit_reference::value struct cast_ratio { using type = ratio_divide; }; template struct cast_ratio { using from_ratio = ratio_multiply; using to_ratio = ratio_multiply; using type = ratio_divide; }; } // 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 [[nodiscard]] constexpr auto quantity_cast(const quantity& q) requires QuantityOf { using c_ratio = detail::cast_ratio::type; 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 [[nodiscard]] constexpr auto quantity_cast(const quantity& q) requires equivalent_dim { 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 [[nodiscard]] constexpr auto quantity_cast(const quantity& q) requires UnitOf { 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); } } // namespace units