// 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 #include #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(quantity_ratio(QuantityFrom{}) / quantity_ratio(QuantityTo{})); } // 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(!(std::is_same_v && std::is_same_v)) 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_; } [[nodiscard]] static constexpr quantity zero() noexcept requires requires { quantity_values::zero(); } { return quantity(quantity_values::zero()); } [[nodiscard]] static constexpr quantity one() noexcept requires requires { quantity_values::one(); } { return quantity(quantity_values::one()); } [[nodiscard]] static constexpr quantity min() noexcept requires requires { quantity_values::min(); } { return quantity(quantity_values::min()); } [[nodiscard]] static constexpr quantity max() noexcept requires requires { quantity_values::max(); } { return quantity(quantity_values::max()); } [[nodiscard]] constexpr quantity operator+() const { return *this; } [[nodiscard]] constexpr quantity operator-() const requires std::regular_invocable, rep> { return quantity(-count()); } constexpr quantity& operator++() requires requires(rep v) { { ++v } -> std::same_as; } { ++value_; return *this; } [[nodiscard]] constexpr quantity operator++(int) requires requires(rep v) { { v++ } -> std::same_as; } { return quantity(value_++); } constexpr quantity& operator--() requires requires(rep v) { { --v } -> std::same_as; } { --value_; return *this; } [[nodiscard]] constexpr quantity operator--(int) requires requires(rep v) { { v-- } -> std::same_as; } { 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 requires (!treat_as_floating_point) && (!treat_as_floating_point) constexpr quantity& operator%=(const Value& rhs) requires requires(rep v1, Value v2) { { v1 %= v2 } -> std::same_as; } { value_ %= rhs; return *this; } constexpr quantity& operator%=(const quantity& q) requires (!treat_as_floating_point) && requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as; } { value_ %= q.count(); return *this; } // Hidden Friends // Below friend functions are to be found via argument-dependent lookup only [[nodiscard]] friend constexpr quantity operator+(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep, Rep> { return quantity(lhs.count() + rhs.count()); } template requires std::regular_invocable, Rep, Rep2> [[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const quantity& rhs) { using common_rep = decltype(lhs.count() + rhs.count()); using ret = common_quantity, common_rep>; return ret(ret(lhs).count() + ret(rhs).count()); } [[nodiscard]] friend constexpr quantity operator-(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep, Rep> { return quantity(lhs.count() - rhs.count()); } template requires std::regular_invocable, Rep, Rep2> [[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const quantity& rhs) { using common_rep = decltype(lhs.count() - rhs.count()); using ret = common_quantity, common_rep>; return ret(ret(lhs).count() - ret(rhs).count()); } template requires std::regular_invocable, Rep, Value> [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v) { using common_rep = decltype(q.count() * v); using ret = quantity; return ret(q.count() * v); } template requires std::regular_invocable, Value, Rep> [[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q) { return q * v; } template requires std::regular_invocable, Rep, Rep2> [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& lhs, const quantity& rhs) { using dim = dimension_multiply; using ret_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 requires std::regular_invocable, Value, Rep> [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) { Expects(q.count() != 0); using dim = dim_invert; using ret_unit = downcast_unit; using common_rep = decltype(v / q.count()); using ret = quantity; return ret(v / q.count()); } template requires std::regular_invocable, Rep, Value> [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v) { Expects(v != Value{0}); using common_rep = decltype(q.count() / v); using ret = quantity; return ret(q.count() / v); } template requires std::regular_invocable, Rep, Rep2> [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& lhs, const quantity& rhs) { Expects(rhs.count() != 0); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide; using ret_unit = downcast_unit::ratio) / (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; using ret = quantity; return ret(lhs.count() / rhs.count()); } template requires (!treat_as_floating_point) && (!treat_as_floating_point) && std::regular_invocable, Rep, Value> [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v) { using common_rep = decltype(q.count() % v); using ret = quantity; return ret(q.count() % v); } template requires (!treat_as_floating_point) && (!treat_as_floating_point) && std::regular_invocable, Rep, Rep2> [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) { using common_rep = decltype(lhs.count() % rhs.count()); using ret = common_quantity, common_rep>; return ret(ret(lhs).count() % ret(rhs).count()); } template requires equivalent_dim && std::three_way_comparable_with [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) { using cq = common_quantity>; return cq(lhs).count() <=> cq(rhs).count(); } template requires equivalent_dim && std::equality_comparable_with [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) { using cq = common_quantity>; return cq(lhs).count() == cq(rhs).count(); } template friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { return os << detail::to_string(q); } }; namespace detail { template inline constexpr bool is_quantity> = true; } // namespace detail } // namespace units