// 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 namespace std::experimental::units { // is_quantity namespace detail { template inline constexpr bool is_quantity = false; // partial specialization below after the first quantity forward declaration } template concept bool Quantity = detail::is_quantity; template concept bool QuantityOf = Quantity && Dimension && std::same_as; // Scalar template concept bool Scalar = Number && !Quantity; template class quantity; namespace detail { template inline constexpr bool is_quantity> = true; } // namespace detail // common_quantity template struct common_quantity; template struct common_quantity, quantity, Rep> { using type = quantity; }; template requires std::same_as struct common_quantity, quantity, Rep> { using type = quantity>>, Rep>; }; template> using common_quantity_t = common_quantity::type; // treat_as_floating_point template // todo Conceptify that inline constexpr bool treat_as_floating_point = std::is_floating_point_v; // quantity_cast namespace detail { template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(static_cast(q.count()) * static_cast(CR::num) / static_cast(CR::den))); } }; 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) { return To(static_cast(static_cast(q.count()) / static_cast(CR::den))); } }; template struct quantity_cast_impl { template static constexpr To cast(const Q& q) { return To(static_cast(static_cast(q.count()) * static_cast(CR::num))); } }; } // namespace detail template requires std::same_as constexpr To quantity_cast(const quantity& q) { using c_ratio = ratio_divide; using c_rep = std::common_type_t; using cast = detail::quantity_cast_impl; return cast::cast(q); } template constexpr quantity quantity_cast(const quantity& q) { return quantity_cast>(q); } // quantity_values template struct quantity_values { static constexpr Rep zero() noexcept { return Rep(0); } static constexpr Rep one() noexcept { return Rep(1); } static constexpr Rep max() noexcept { return std::numeric_limits::max(); } static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } }; // quantity template class quantity { Rep value_; public: using unit = U; using rep = Rep; using dimension = U::dimension; static_assert(!Quantity, "rep cannot be a quantity"); quantity() = default; quantity(const quantity&) = default; template Rep2> requires treat_as_floating_point || (!treat_as_floating_point) constexpr explicit quantity(const Rep2& r) : value_{static_cast(r)} { } template requires std::same_as && std::convertible_to && (treat_as_floating_point || (std::ratio_divide::den == 1 && !treat_as_floating_point)) constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} { } quantity& operator=(const quantity& other) = default; [[nodiscard]] constexpr rep count() const noexcept { return value_; } [[nodiscard]] static constexpr quantity zero() noexcept { return quantity(quantity_values::zero()); } [[nodiscard]] static constexpr quantity one() noexcept { return quantity(quantity_values::one()); } [[nodiscard]] static constexpr quantity min() noexcept { return quantity(quantity_values::min()); } [[nodiscard]] static constexpr quantity max() noexcept { return quantity(quantity_values::max()); } [[nodiscard]] constexpr quantity operator+() const { return quantity(*this); } [[nodiscard]] constexpr quantity operator-() const { return quantity(-count()); } constexpr quantity& operator++() { ++value_; return *this; } constexpr quantity operator++(int) { return quantity(value_++); } constexpr quantity& operator--() { --value_; return *this; } constexpr quantity operator--(int) { return quantity(value_--); } constexpr quantity& operator+=(const quantity& q) { value_ += q.count(); return *this; } constexpr quantity& operator-=(const quantity& q) { value_ -= q.count(); return *this; } constexpr quantity& operator*=(const rep& rhs) { value_ *= rhs; return *this; } constexpr quantity& operator/=(const rep& rhs) { value_ /= rhs; return *this; } constexpr quantity& operator%=(const rep& rhs) { value_ %= rhs; return *this; } constexpr quantity& operator%=(const quantity& q) { value_ %= q.count(); return *this; } }; // clang-format off template [[nodiscard]] constexpr Quantity operator+(const quantity& lhs, const quantity& rhs) requires std::same_as { using common_rep = decltype(lhs.count() + rhs.count()); using ret = common_quantity_t, quantity, common_rep>; return ret(ret(lhs).count() + ret(rhs).count()); } template [[nodiscard]] constexpr Quantity operator-(const quantity& lhs, const quantity& rhs) requires std::same_as { using common_rep = decltype(lhs.count() - rhs.count()); using ret = common_quantity_t, quantity, common_rep>; return ret(ret(lhs).count() - ret(rhs).count()); } // template template [[nodiscard]] constexpr Quantity operator*(const quantity& q, const Rep2& v) requires (!Quantity) { using common_rep = decltype(q.count()* v); using ret = quantity; return ret(ret(q).count() * v); } // template template [[nodiscard]] constexpr Quantity operator*(const Rep1& v, const quantity& q) requires (!Quantity) { return q * v; } template [[nodiscard]] constexpr Scalar operator*(const quantity& lhs, const quantity& rhs) requires std::same_as> { using common_rep = decltype(lhs.count() * rhs.count()); using ratio = ratio_multiply; return common_rep(lhs.count()) * common_rep(rhs.count()) * common_rep(ratio::num) / common_rep(ratio::den); } template [[nodiscard]] constexpr Quantity operator*(const quantity& lhs, const quantity& rhs) requires (!std::same_as>) && (treat_as_floating_point || (std::ratio_multiply::den == 1)) { using dim = dimension_multiply_t; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity>>, common_rep>; return ret(lhs.count() * rhs.count()); } // template template [[nodiscard]] constexpr Quantity operator/(const Rep1& v, const quantity& q) requires (!Quantity) { Expects(q != std::remove_cvref_t(0)); using dim = dim_invert_t; using common_rep = decltype(v / q.count()); using ret = quantity>>, common_rep>; using den = quantity; return ret(v / den(q).count()); } // template template [[nodiscard]] constexpr Quantity operator/(const quantity& q, const Rep2& v) requires (!Quantity) { Expects(v != Rep2{0}); using common_rep = decltype(q.count() / v); using ret = quantity; return ret(ret(q).count() / v); } template [[nodiscard]] constexpr Scalar operator/(const quantity& lhs, const quantity& rhs) requires std::same_as { Expects(rhs != std::remove_cvref_t(0)); using common_rep = decltype(lhs.count() / rhs.count()); using cq = common_quantity_t, quantity, common_rep>; return cq(lhs).count() / cq(rhs).count(); } template [[nodiscard]] constexpr Quantity operator/(const quantity& lhs, const quantity& rhs) requires (!std::same_as) && (treat_as_floating_point || (ratio_divide::den == 1)) { Expects(rhs != std::remove_cvref_t(0)); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide_t; using ret = quantity>>, common_rep>; return ret(lhs.count() / rhs.count()); } template [[nodiscard]] constexpr Quantity operator%(const quantity& q, const Rep2& v) { using common_rep = decltype(q.count() % v); using ret = quantity; return ret(ret(q).count() % v); } template [[nodiscard]] constexpr Quantity operator%(const quantity& lhs, const quantity& rhs) { using common_rep = decltype(lhs.count() % rhs.count()); using ret = common_quantity_t, quantity, common_rep>; return ret(ret(lhs).count() % ret(rhs).count()); } // clang-format on template [[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) requires std::same_as { using ct = common_quantity_t, quantity>; return ct(lhs).count() == ct(rhs).count(); } template [[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) requires std::same_as { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) requires std::same_as { using ct = common_quantity_t, quantity>; return ct(lhs).count() < ct(rhs).count(); } template [[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) requires std::same_as { return !(rhs < lhs); } template [[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) requires std::same_as { return rhs < lhs; } template [[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) requires std::same_as { return !(lhs < rhs); } } // namespace std::experimental::units