// 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 #if COMP_MSVC || COMP_GCC >= 10 #include #endif #include namespace units { namespace detail { template concept safe_convertible = // exposition only std::convertible_to && (treat_as_floating_point || (!treat_as_floating_point)); template concept safe_divisible = // exposition only treat_as_floating_point || is_integral(UnitFrom::ratio / UnitTo::ratio); } // namespace detail /** * @brief A quantity * * Property of a phenomenon, body, or substance, where the property has a magnitude that can be * expressed by means of a number and a measurement unit. * * @tparam D a dimension of the quantity (can be either a BaseDimension or a DerivedDimension) * @tparam U a measurement unit of the quantity * @tparam Rep a type to be used to represent values of a quantity */ template U, Scalar Rep = double> class quantity { Rep value_{}; public: using dimension = D; using unit = U; using rep = Rep; quantity() = default; quantity(const quantity&) = default; quantity(quantity&&) = default; template requires detail::safe_convertible constexpr explicit quantity(const Value& v) : value_{static_cast(v)} {} template requires equivalent_dim && detail::safe_convertible && detail::safe_divisible constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} {} quantity& operator=(const quantity&) = default; quantity& operator=(quantity&&) = default; [[nodiscard]] constexpr rep count() const noexcept { return value_; } template [[nodiscard]] static constexpr quantity zero() noexcept requires requires { quantity_values::zero(); } // requires requires { quantity_values::zero(); } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(quantity_values::zero()); } template [[nodiscard]] static constexpr quantity one() noexcept requires requires { quantity_values::one(); } // requires requires { quantity_values::one(); } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(quantity_values::one()); } template [[nodiscard]] static constexpr quantity min() noexcept requires requires { quantity_values::min(); } // requires requires { quantity_values::min(); } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(quantity_values::min()); } template [[nodiscard]] static constexpr quantity max() noexcept requires requires { quantity_values::max(); } // requires requires { quantity_values::max(); } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(quantity_values::max()); } [[nodiscard]] constexpr quantity operator+() const { return *this; } template [[nodiscard]] constexpr quantity operator-() const requires std::regular_invocable, T> // requires std::regular_invocable, rep> // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(-count()); } template constexpr quantity& operator++() requires requires(T v) { { ++v } -> SAME_AS(T&); } // requires requires(rep v) { { ++v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { ++value_; return *this; } template [[nodiscard]] constexpr quantity operator++(int) requires requires(T v) { { v++ } -> SAME_AS(T); } // requires requires(rep v) { { v++ } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(value_++); } template constexpr quantity& operator--() requires requires(T v) { { --v } -> SAME_AS(T&); } // requires requires(rep v) { { --v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { --value_; return *this; } template [[nodiscard]] constexpr quantity operator--(int) requires requires(T v) { { v-- } -> SAME_AS(T); } // requires requires(rep v) { { v-- } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { return quantity(value_--); } template requires detail::safe_convertible constexpr quantity& operator+=(const quantity& q) { value_ += q.count(); return *this; } template requires detail::safe_convertible constexpr quantity& operator-=(const quantity& q) { value_ -= q.count(); return *this; } template requires detail::safe_convertible constexpr quantity& operator*=(const Rep2& rhs) { value_ *= rhs; return *this; } template requires detail::safe_convertible constexpr quantity& operator/=(const Rep2& rhs) { value_ /= rhs; return *this; } template constexpr quantity& operator%=(const Value& rhs) requires (!treat_as_floating_point) && (!treat_as_floating_point) && requires(T v1, Value v2) { { v1 %= v2 } -> SAME_AS(T&); } // requires(rep v1, Value v2) { { v1 %= v2 } -> SAME_AS(rep&); } // TODO gated by gcc-9 (fixed in gcc-10) { value_ %= rhs; return *this; } template constexpr quantity& operator%=(const quantity& q) requires (!treat_as_floating_point) && requires(T v1, T v2) { { v1 %= v2 } -> SAME_AS(T&); } // requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { value_ %= q.count(); return *this; } // Hidden Friends // Below friend functions are to be found via argument-dependent lookup only #if COMP_MSVC || COMP_GCC >= 10 template [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::three_way_comparable_with { using cq = common_quantity>; return cq(lhs).count() <=> cq(rhs).count(); } template [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::equality_comparable_with { using cq = common_quantity>; return cq(lhs).count() == cq(rhs).count(); } #else template [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::equality_comparable_with { using cq = common_quantity>; return cq(lhs).count() == cq(rhs).count(); } template [[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::equality_comparable_with { return !(lhs == rhs); } template [[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::totally_ordered_with { using cq = common_quantity>; return cq(lhs).count() < cq(rhs).count(); } template [[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::totally_ordered_with { return !(rhs < lhs); } template [[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::totally_ordered_with { return rhs < lhs; } template [[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity& rhs) requires equivalent_dim && std::totally_ordered_with { return !(lhs < rhs); } #endif template friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { return os << detail::to_string(q); } }; template [[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> { using common_rep = decltype(lhs.count() + rhs.count()); using ret = common_quantity, quantity, common_rep>; return ret(ret(lhs).count() + ret(rhs).count()); } template [[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> { using common_rep = decltype(lhs.count() - rhs.count()); using ret = common_quantity, quantity, common_rep>; return ret(ret(lhs).count() - ret(rhs).count()); } template [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) requires std::regular_invocable, Rep, Value> { using common_rep = decltype(q.count() * v); using ret = quantity; return ret(q.count() * v); } template [[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) requires std::regular_invocable, Value, Rep> { return q * v; } template [[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> && equivalent_dim> { using common_rep = decltype(lhs.count() * rhs.count()); const ratio r = U1::ratio * U2::ratio; if constexpr (treat_as_floating_point) { return lhs.count() * rhs.count() * static_cast(r.num * detail::fpow10(r.exp)) / static_cast(r.den); } else { return lhs.count() * rhs.count() * static_cast(r.num * detail::ipow10(r.exp)) / static_cast(r.den); } } template [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> { using dim = dimension_multiply; using unit = downcast_unit::ratio) * (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity; return ret(lhs.count() * rhs.count()); } template [[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) requires std::regular_invocable, Value, Rep> { Expects(q.count() != 0); using dim = dim_invert; using unit = downcast_unit; using common_rep = decltype(v / q.count()); using ret = quantity; return ret(v / q.count()); } template [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& q, const Value& v) requires std::regular_invocable, Rep, Value> { Expects(v != Value{0}); using common_rep = decltype(q.count() / v); using ret = quantity; return ret(q.count() / v); } template [[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> && equivalent_dim { Expects(rhs.count() != 0); using common_rep = decltype(lhs.count() / rhs.count()); using cq = common_quantity, quantity, common_rep>; return cq(lhs).count() / cq(rhs).count(); } template [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> { Expects(rhs.count() != 0); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide; using unit = downcast_unit::ratio) / (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; using ret = quantity; return ret(lhs.count() / rhs.count()); } template [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) requires (!treat_as_floating_point) && (!treat_as_floating_point) && std::regular_invocable, Rep, Value> { using common_rep = decltype(q.count() % v); using ret = quantity; return ret(q.count() % v); } template [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) requires (!treat_as_floating_point) && (!treat_as_floating_point) && std::regular_invocable, Rep1, Rep2> { using common_rep = decltype(lhs.count() % rhs.count()); using ret = common_quantity, quantity, common_rep>; return ret(ret(lhs).count() % ret(rhs).count()); } namespace detail { template inline constexpr bool is_quantity> = true; } // namespace detail } // namespace units