mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-05 05:04:27 +02:00
Overconstrained quantity operations relaxed
This commit is contained in:
@@ -3,6 +3,38 @@
|
|||||||
Using Custom Representation Types
|
Using Custom Representation Types
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
|
A custom representation type can be provided as the last `quantity` class template parameter.
|
||||||
|
With this a user is able to change how a quantity's value is being represented and provide
|
||||||
|
its own custom logic for it (i.e. use a complex number or a measurement class that will handle
|
||||||
|
not only a value but also a measurement error).
|
||||||
|
|
||||||
|
|
||||||
|
A `Scalar` concept
|
||||||
|
------------------
|
||||||
|
|
||||||
|
To support a minimum set of `quantity` operations all custom representation types have to
|
||||||
|
satisfy at least the `Scalar` concept. Which means that they:
|
||||||
|
|
||||||
|
- cannot be quantities or be wrappers over the `quantity` type
|
||||||
|
(i.e. ``std::optional<si::length<si::metre>>``),
|
||||||
|
- have to be regular types (e.g. they have to provide equality operators)
|
||||||
|
- must be constructible from a fundamental integral type (to
|
||||||
|
|
||||||
|
With the above we will be able to construct quantities, convert between the units of the same
|
||||||
|
dimension and compare them for equality. To provide additional `quantity` operations the
|
||||||
|
custom representation type have to satisfy more requirements.
|
||||||
|
|
||||||
|
|
||||||
|
Additional requirements
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
|
||||||
|
The requirements described in the chapter are optional in a meaning that if someone does
|
||||||
|
not plan to use a specific quantity's operation his/her custom representation type can
|
||||||
|
ignore (not implement/satisfy) the requirements for it.
|
||||||
|
|
||||||
|
|
||||||
Construction of Quantities with Custom Representation Types
|
Construction of Quantities with Custom Representation Types
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
@@ -49,7 +81,10 @@ from a regular quantity value::
|
|||||||
Conversions of Quantities with Custom Representation Types
|
Conversions of Quantities with Custom Representation Types
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
Again let's assume two types but this time let's scope on converting operators rather
|
In case we want to mix quantities of our Custom Representation Type with the quantities using
|
||||||
|
fundamental arithmetic types as their representation we have to provide conversion operators.
|
||||||
|
|
||||||
|
Again let's assume two types but this time let's scope on conversion operators rather
|
||||||
than on constructors:
|
than on constructors:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
@@ -89,14 +124,21 @@ representation types with::
|
|||||||
si::length<si::metre, int> d3(quantity_cast<int>(d_expl)); // OK
|
si::length<si::metre, int> d3(quantity_cast<int>(d_expl)); // OK
|
||||||
|
|
||||||
|
|
||||||
|
Tricky cases
|
||||||
|
------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Customization points
|
Customization points
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
treat_as_floating_point
|
||||||
|
|
||||||
|
quantity_value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
For more examples of custom representation types usage please refer to
|
For more examples of custom representation types usage please refer to :ref:`measurement`
|
||||||
:ref:`Linear Algebra of Quantities` chapter and :ref:`measurement` example.
|
example.
|
||||||
|
@@ -31,17 +31,6 @@
|
|||||||
|
|
||||||
namespace units {
|
namespace units {
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template<typename T, typename U = T>
|
|
||||||
concept basic_arithmetic = // exposition only
|
|
||||||
std::magma<std::ranges::plus, T, U> &&
|
|
||||||
std::magma<std::ranges::minus, T, U> &&
|
|
||||||
std::magma<std::ranges::times, T, U> &&
|
|
||||||
std::magma<std::ranges::divided_by, T, U>;
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// PrefixFamily
|
// PrefixFamily
|
||||||
struct prefix_family;
|
struct prefix_family;
|
||||||
|
|
||||||
@@ -265,6 +254,14 @@ concept WrappedQuantity = detail::is_wrapped_quantity<T>;
|
|||||||
* Satisfied by types that satisfy `(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>`.
|
* Satisfied by types that satisfy `(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>`.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
concept Scalar = (!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>; // TODO: && std::totally_ordered<T>;// && detail::basic_arithmetic<T>;
|
concept Scalar =
|
||||||
|
(!Quantity<T>) &&
|
||||||
|
(!WrappedQuantity<T>) &&
|
||||||
|
std::regular<T> &&
|
||||||
|
// construction from an integral type
|
||||||
|
std::constructible_from<T, std::int64_t> &&
|
||||||
|
// unit scaling
|
||||||
|
std::regular_invocable<std::multiplies<>, T, T> &&
|
||||||
|
std::regular_invocable<std::divides<>, T, T>;
|
||||||
|
|
||||||
} // namespace units
|
} // namespace units
|
||||||
|
@@ -228,7 +228,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::totally_ordered_with<Rep, Rep2>
|
std::totally_ordered_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
||||||
@@ -238,7 +237,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr auto operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr auto operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::equality_comparable_with<Rep, Rep2>
|
std::equality_comparable_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
||||||
@@ -250,7 +248,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::equality_comparable_with<Rep, Rep2>
|
std::equality_comparable_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
||||||
@@ -260,7 +257,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::equality_comparable_with<Rep, Rep2>
|
std::equality_comparable_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
@@ -269,7 +265,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::totally_ordered_with<Rep, Rep2>
|
std::totally_ordered_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
|
||||||
@@ -279,7 +274,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::totally_ordered_with<Rep, Rep2>
|
std::totally_ordered_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
return !(rhs < lhs);
|
return !(rhs < lhs);
|
||||||
@@ -288,7 +282,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::totally_ordered_with<Rep, Rep2>
|
std::totally_ordered_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
return rhs < lhs;
|
return rhs < lhs;
|
||||||
@@ -297,7 +290,6 @@ public:
|
|||||||
template<typename D2, typename U2, typename Rep2>
|
template<typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires equivalent_dim<D, D2> &&
|
requires equivalent_dim<D, D2> &&
|
||||||
detail::basic_arithmetic<Rep, Rep2> &&
|
|
||||||
std::totally_ordered_with<Rep, Rep2>
|
std::totally_ordered_with<Rep, Rep2>
|
||||||
{
|
{
|
||||||
return !(lhs < rhs);
|
return !(lhs < rhs);
|
||||||
@@ -314,7 +306,7 @@ public:
|
|||||||
|
|
||||||
template<typename D, typename U1, typename Rep1, typename U2, typename Rep2>
|
template<typename D, typename U1, typename Rep1, typename U2, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator+(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[nodiscard]] constexpr Quantity AUTO operator+(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
||||||
requires detail::basic_arithmetic<Rep1, Rep2>
|
requires std::regular_invocable<std::plus<>, Rep1, Rep2>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(lhs.count() + rhs.count());
|
using common_rep = decltype(lhs.count() + rhs.count());
|
||||||
using ret = common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
|
using ret = common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
|
||||||
@@ -323,7 +315,7 @@ template<typename D, typename U1, typename Rep1, typename U2, typename Rep2>
|
|||||||
|
|
||||||
template<typename D, typename U1, typename Rep1, typename U2, typename Rep2>
|
template<typename D, typename U1, typename Rep1, typename U2, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator-(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[nodiscard]] constexpr Quantity AUTO operator-(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
||||||
requires detail::basic_arithmetic<Rep1, Rep2>
|
requires std::regular_invocable<std::minus<>, Rep1, Rep2>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(lhs.count() - rhs.count());
|
using common_rep = decltype(lhs.count() - rhs.count());
|
||||||
using ret = common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
|
using ret = common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
|
||||||
@@ -332,7 +324,7 @@ template<typename D, typename U1, typename Rep1, typename U2, typename Rep2>
|
|||||||
|
|
||||||
template<typename D, typename U, typename Rep, Scalar Value>
|
template<typename D, typename U, typename Rep, Scalar Value>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator*(const quantity<D, U, Rep>& q, const Value& v)
|
[[nodiscard]] constexpr Quantity AUTO operator*(const quantity<D, U, Rep>& q, const Value& v)
|
||||||
requires std::magma<std::ranges::times, Rep, Value>
|
requires std::regular_invocable<std::multiplies<>, Rep, Value>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(q.count() * v);
|
using common_rep = decltype(q.count() * v);
|
||||||
using ret = quantity<D, U, common_rep>;
|
using ret = quantity<D, U, common_rep>;
|
||||||
@@ -341,14 +333,15 @@ template<typename D, typename U, typename Rep, Scalar Value>
|
|||||||
|
|
||||||
template<Scalar Value, typename D, typename U, typename Rep>
|
template<Scalar Value, typename D, typename U, typename Rep>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity<D, U, Rep>& q)
|
[[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity<D, U, Rep>& q)
|
||||||
requires std::magma<std::ranges::times, Value, Rep>
|
requires std::regular_invocable<std::multiplies<>, Value, Rep>
|
||||||
{
|
{
|
||||||
return q * v;
|
return q * v;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] constexpr Scalar AUTO operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] constexpr Scalar AUTO operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires detail::basic_arithmetic<Rep1, Rep2> && equivalent_dim<D1, dim_invert<D2>>
|
requires std::regular_invocable<std::multiplies<>, Rep1, Rep2> &&
|
||||||
|
equivalent_dim<D1, dim_invert<D2>>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(lhs.count() * rhs.count());
|
using common_rep = decltype(lhs.count() * rhs.count());
|
||||||
using ratio = ratio_multiply<typename U1::ratio, typename U2::ratio>;
|
using ratio = ratio_multiply<typename U1::ratio, typename U2::ratio>;
|
||||||
@@ -361,7 +354,8 @@ template<typename D1, typename U1, typename Rep1, typename D2, typename U2, type
|
|||||||
|
|
||||||
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] constexpr Quantity AUTO operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires detail::basic_arithmetic<Rep1, Rep2> && (!equivalent_dim<D1, dim_invert<D2>>)
|
requires std::regular_invocable<std::multiplies<>, Rep1, Rep2> &&
|
||||||
|
(!equivalent_dim<D1, dim_invert<D2>>)
|
||||||
{
|
{
|
||||||
using dim = dimension_multiply<D1, D2>;
|
using dim = dimension_multiply<D1, D2>;
|
||||||
using ratio1 = ratio_divide<typename U1::ratio, typename dimension_unit<D1>::ratio>;
|
using ratio1 = ratio_divide<typename U1::ratio, typename dimension_unit<D1>::ratio>;
|
||||||
@@ -375,7 +369,7 @@ template<typename D1, typename U1, typename Rep1, typename D2, typename U2, type
|
|||||||
|
|
||||||
template<Scalar Value, typename D, typename U, typename Rep>
|
template<Scalar Value, typename D, typename U, typename Rep>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity<D, U, Rep>& q)
|
[[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity<D, U, Rep>& q)
|
||||||
requires std::magma<std::ranges::divided_by, Value, Rep>
|
requires std::regular_invocable<std::divides<>, Value, Rep>
|
||||||
{
|
{
|
||||||
Expects(q.count() != 0);
|
Expects(q.count() != 0);
|
||||||
|
|
||||||
@@ -389,7 +383,7 @@ template<Scalar Value, typename D, typename U, typename Rep>
|
|||||||
|
|
||||||
template<typename D, typename U, typename Rep, Scalar Value>
|
template<typename D, typename U, typename Rep, Scalar Value>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator/(const quantity<D, U, Rep>& q, const Value& v)
|
[[nodiscard]] constexpr Quantity AUTO operator/(const quantity<D, U, Rep>& q, const Value& v)
|
||||||
requires std::magma<std::ranges::divided_by, Rep, Value>
|
requires std::regular_invocable<std::divides<>, Rep, Value>
|
||||||
{
|
{
|
||||||
Expects(v != Value{0});
|
Expects(v != Value{0});
|
||||||
|
|
||||||
@@ -400,7 +394,8 @@ template<typename D, typename U, typename Rep, Scalar Value>
|
|||||||
|
|
||||||
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] constexpr Scalar AUTO operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] constexpr Scalar AUTO operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires detail::basic_arithmetic<Rep1, Rep2> && equivalent_dim<D1, D2>
|
requires std::regular_invocable<std::divides<>, Rep1, Rep2> &&
|
||||||
|
equivalent_dim<D1, D2>
|
||||||
{
|
{
|
||||||
Expects(rhs.count() != 0);
|
Expects(rhs.count() != 0);
|
||||||
|
|
||||||
@@ -411,7 +406,8 @@ template<typename D1, typename U1, typename Rep1, typename D2, typename U2, type
|
|||||||
|
|
||||||
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity AUTO operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
[[nodiscard]] constexpr Quantity AUTO operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||||
requires detail::basic_arithmetic<Rep1, Rep2> && (!equivalent_dim<D1, D2>)
|
requires std::regular_invocable<std::divides<>, Rep1, Rep2> &&
|
||||||
|
(!equivalent_dim<D1, D2>)
|
||||||
{
|
{
|
||||||
Expects(rhs.count() != 0);
|
Expects(rhs.count() != 0);
|
||||||
|
|
||||||
@@ -429,7 +425,7 @@ template<typename D, typename U, typename Rep, Scalar Value>
|
|||||||
[[nodiscard]] constexpr Quantity AUTO operator%(const quantity<D, U, Rep>& q, const Value& v)
|
[[nodiscard]] constexpr Quantity AUTO operator%(const quantity<D, U, Rep>& q, const Value& v)
|
||||||
requires (!treat_as_floating_point<Rep>) &&
|
requires (!treat_as_floating_point<Rep>) &&
|
||||||
(!treat_as_floating_point<Value>) &&
|
(!treat_as_floating_point<Value>) &&
|
||||||
std::magma<std::ranges::modulus, Rep, Value>
|
std::regular_invocable<std::modulus<>, Rep, Value>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(q.count() % v);
|
using common_rep = decltype(q.count() % v);
|
||||||
using ret = quantity<D, U, common_rep>;
|
using ret = quantity<D, U, common_rep>;
|
||||||
@@ -440,7 +436,7 @@ template<typename D, typename U1, typename Rep1, typename U2, typename Rep2>
|
|||||||
[[nodiscard]] constexpr Quantity AUTO operator%(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[nodiscard]] constexpr Quantity AUTO operator%(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
||||||
requires (!treat_as_floating_point<Rep1>) &&
|
requires (!treat_as_floating_point<Rep1>) &&
|
||||||
(!treat_as_floating_point<Rep2>) &&
|
(!treat_as_floating_point<Rep2>) &&
|
||||||
std::magma<std::ranges::modulus, Rep1, Rep2>
|
std::regular_invocable<std::modulus<>, Rep1, Rep2>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(lhs.count() % rhs.count());
|
using common_rep = decltype(lhs.count() % rhs.count());
|
||||||
using ret = common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
|
using ret = common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
|
||||||
|
@@ -163,11 +163,10 @@ struct cast_ratio<FromD, FromU, ToD, ToU> {
|
|||||||
*/
|
*/
|
||||||
template<Quantity To, typename D, typename U, typename Rep>
|
template<Quantity To, typename D, typename U, typename Rep>
|
||||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||||
requires QuantityOf<To, D> &&
|
requires QuantityOf<To, D>
|
||||||
detail::basic_arithmetic<std::common_type_t<typename To::rep, Rep, intmax_t>>
|
|
||||||
{
|
{
|
||||||
using c_ratio = detail::cast_ratio<D, U, typename To::dimension, typename To::unit>::type;
|
using c_ratio = detail::cast_ratio<D, U, typename To::dimension, typename To::unit>::type;
|
||||||
using c_rep = std::common_type_t<typename To::rep, Rep, intmax_t>;
|
using c_rep = std::common_type_t<typename To::rep, Rep>;
|
||||||
using ret_unit = downcast_unit<typename To::dimension, typename To::unit::ratio>;
|
using ret_unit = downcast_unit<typename To::dimension, typename To::unit::ratio>;
|
||||||
using ret = quantity<typename To::dimension, ret_unit, typename To::rep>;
|
using ret = quantity<typename To::dimension, ret_unit, typename To::rep>;
|
||||||
using cast = detail::quantity_cast_impl<ret, c_ratio, c_rep, c_ratio::num == 1 && c_ratio::exp == 0, c_ratio::den == 1 && c_ratio::exp == 0>;
|
using cast = detail::quantity_cast_impl<ret, c_ratio, c_rep, c_ratio::num == 1 && c_ratio::exp == 0, c_ratio::den == 1 && c_ratio::exp == 0>;
|
||||||
@@ -226,7 +225,6 @@ template<Unit ToU, typename D, typename U, typename Rep>
|
|||||||
*/
|
*/
|
||||||
template<Scalar ToRep, typename D, typename U, typename Rep>
|
template<Scalar ToRep, typename D, typename U, typename Rep>
|
||||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||||
requires detail::basic_arithmetic<std::common_type_t<ToRep, Rep, intmax_t>>
|
|
||||||
{
|
{
|
||||||
return quantity_cast<quantity<D, U, ToRep>>(q);
|
return quantity_cast<quantity<D, U, ToRep>>(q);
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
add_library(unit_tests_static
|
add_library(unit_tests_static
|
||||||
cgs_test.cpp
|
cgs_test.cpp
|
||||||
custom_rep_test.cpp
|
custom_rep_test_min_req.cpp
|
||||||
custom_unit_test.cpp
|
custom_unit_test.cpp
|
||||||
data_test.cpp
|
data_test.cpp
|
||||||
dimension_op_test.cpp
|
dimension_op_test.cpp
|
||||||
|
@@ -32,37 +32,48 @@ using namespace units;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct arithmetic_ops {
|
struct equality_ops {
|
||||||
// constexpr T& operator+=(T other) { value_ += other.value_; return *this; }
|
[[nodiscard]] friend constexpr bool operator==(T lhs, T rhs) { return lhs.value_ == rhs.value_; }
|
||||||
// constexpr T& operator-=(T other) { value_ -= other.value_; return *this; }
|
[[nodiscard]] friend constexpr bool operator!=(T lhs, T rhs) { return !(lhs == rhs); }
|
||||||
// constexpr T& operator*=(T other) { value_ *= other.value_; return *this; }
|
};
|
||||||
// constexpr T& operator/=(T other) { value_ /= other.value_; return *this; }
|
|
||||||
|
|
||||||
// [[nodiscard]] constexpr T operator-() const { return T(-value_); }
|
template<typename T>
|
||||||
|
struct scaling_ops {
|
||||||
[[nodiscard]] friend constexpr T operator+(T lhs, T rhs) {
|
|
||||||
return T(lhs.value_ + rhs.value_);
|
|
||||||
}
|
|
||||||
[[nodiscard]] friend constexpr T operator-(T lhs, T rhs) {
|
|
||||||
return T(lhs.value_ - rhs.value_);
|
|
||||||
}
|
|
||||||
[[nodiscard]] friend constexpr T operator*(T lhs, T rhs) {
|
[[nodiscard]] friend constexpr T operator*(T lhs, T rhs) {
|
||||||
return T(lhs.value_ * rhs.value_);
|
return T(lhs.value_ * rhs.value_);
|
||||||
}
|
}
|
||||||
[[nodiscard]] friend constexpr T operator/(T lhs, T rhs) {
|
[[nodiscard]] friend constexpr T operator/(T lhs, T rhs) {
|
||||||
return T(lhs.value_ / rhs.value_);
|
return T(lhs.value_ / rhs.value_);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] friend constexpr bool operator==(T lhs, T rhs) { return lhs.value_ == rhs.value_; }
|
|
||||||
[[nodiscard]] friend constexpr bool operator!=(T lhs, T rhs) { return !(lhs == rhs); }
|
|
||||||
[[nodiscard]] friend constexpr bool operator<(T lhs, T rhs) { return lhs.value_ < rhs.value_; }
|
|
||||||
[[nodiscard]] friend constexpr bool operator>(T lhs, T rhs) { return rhs < lhs; }
|
|
||||||
[[nodiscard]] friend constexpr bool operator<=(T lhs, T rhs) { return !(rhs < lhs); }
|
|
||||||
[[nodiscard]] friend constexpr bool operator>=(T lhs, T rhs) { return !(lhs < rhs); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct impl_constructible_impl_convertible : arithmetic_ops<impl_constructible_impl_convertible<T>> {
|
struct scalar_ops : equality_ops<T>, scaling_ops<T> {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct impl_constructible : scalar_ops<impl_constructible<T>> {
|
||||||
|
T value_{};
|
||||||
|
impl_constructible() = default;
|
||||||
|
constexpr impl_constructible(T v) : value_(std::move(v)) {}
|
||||||
|
// no conversion to fundamental arithmetic types
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using impl = impl_constructible<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct expl_constructible : scalar_ops<expl_constructible<T>> {
|
||||||
|
T value_{};
|
||||||
|
expl_constructible() = default;
|
||||||
|
constexpr expl_constructible(T v) : value_(std::move(v)) {}
|
||||||
|
// no conversion to fundamental arithmetic types
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using expl = expl_constructible<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct impl_constructible_impl_convertible : scalar_ops<impl_constructible_impl_convertible<T>> /*, int_scaling_ops<impl_constructible_impl_convertible<T>> */ {
|
||||||
T value_{};
|
T value_{};
|
||||||
impl_constructible_impl_convertible() = default;
|
impl_constructible_impl_convertible() = default;
|
||||||
constexpr impl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
|
constexpr impl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
|
||||||
@@ -77,7 +88,7 @@ static_assert(std::convertible_to<impl_impl<float>, float>);
|
|||||||
static_assert(units::Scalar<impl_impl<float>>);
|
static_assert(units::Scalar<impl_impl<float>>);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct expl_constructible_impl_convertible : arithmetic_ops<expl_constructible_impl_convertible<T>> {
|
struct expl_constructible_impl_convertible : scalar_ops<expl_constructible_impl_convertible<T>> {
|
||||||
T value_{};
|
T value_{};
|
||||||
expl_constructible_impl_convertible() = default;
|
expl_constructible_impl_convertible() = default;
|
||||||
constexpr explicit expl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
|
constexpr explicit expl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
|
||||||
@@ -92,7 +103,7 @@ static_assert(std::convertible_to<expl_impl<float>, float>);
|
|||||||
static_assert(units::Scalar<expl_impl<float>>);
|
static_assert(units::Scalar<expl_impl<float>>);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct impl_constructible_expl_convertible : arithmetic_ops<impl_constructible_expl_convertible<T>> {
|
struct impl_constructible_expl_convertible : scalar_ops<impl_constructible_expl_convertible<T>> {
|
||||||
T value_{};
|
T value_{};
|
||||||
impl_constructible_expl_convertible() = default;
|
impl_constructible_expl_convertible() = default;
|
||||||
constexpr impl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
|
constexpr impl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
|
||||||
@@ -107,7 +118,7 @@ static_assert(!std::convertible_to<impl_expl<float>, float>);
|
|||||||
static_assert(units::Scalar<impl_expl<float>>);
|
static_assert(units::Scalar<impl_expl<float>>);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct expl_constructible_expl_convertible : arithmetic_ops<expl_constructible_expl_convertible<T>> {
|
struct expl_constructible_expl_convertible : scalar_ops<expl_constructible_expl_convertible<T>> {
|
||||||
T value_{};
|
T value_{};
|
||||||
expl_constructible_expl_convertible() = default;
|
expl_constructible_expl_convertible() = default;
|
||||||
constexpr explicit expl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
|
constexpr explicit expl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
|
||||||
@@ -125,6 +136,12 @@ static_assert(units::Scalar<expl_expl<float>>);
|
|||||||
|
|
||||||
namespace units {
|
namespace units {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr bool treat_as_floating_point<impl<T>> = std::is_floating_point_v<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr bool treat_as_floating_point<expl_constructible<T>> = std::is_floating_point_v<T>;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline constexpr bool treat_as_floating_point<impl_impl<T>> = std::is_floating_point_v<T>;
|
inline constexpr bool treat_as_floating_point<impl_impl<T>> = std::is_floating_point_v<T>;
|
||||||
|
|
||||||
@@ -138,27 +155,14 @@ template<typename T>
|
|||||||
inline constexpr bool treat_as_floating_point<expl_expl<T>> = std::is_floating_point_v<T>;
|
inline constexpr bool treat_as_floating_point<expl_expl<T>> = std::is_floating_point_v<T>;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct quantity_values<impl_impl<T>> {
|
struct quantity_values<impl<T>> {
|
||||||
static constexpr impl_impl<T> zero() { return impl_impl<T>(0); }
|
static constexpr impl<T> zero() { return 0; }
|
||||||
static constexpr impl_impl<T> max() { return std::numeric_limits<T>::max(); }
|
static constexpr impl<T> max() { return std::numeric_limits<T>::max(); }
|
||||||
static constexpr impl_impl<T> min() { return std::numeric_limits<T>::lowest(); }
|
static constexpr impl<T> min() { return std::numeric_limits<T>::lowest(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace units
|
} // namespace units
|
||||||
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
// template<typename T, typename U>
|
|
||||||
// struct common_type<my_value<T>, my_value<U>> : std::type_identity<my_value<common_type_t<T, U>>> {};
|
|
||||||
|
|
||||||
// template<typename T, typename U>
|
|
||||||
// struct common_type<my_value<T>, U> : common_type<T, U> {};
|
|
||||||
|
|
||||||
// template<typename T, typename U>
|
|
||||||
// struct common_type<T, my_value<U>> : common_type<T, U> {};
|
|
||||||
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using namespace units::si;
|
using namespace units::si;
|
||||||
@@ -225,15 +229,50 @@ static_assert(length<metre, impl_expl<double>>(length<metre, int>(1)).count() ==
|
|||||||
|
|
||||||
// unit conversions
|
// unit conversions
|
||||||
|
|
||||||
|
static_assert(length<metre, impl<int>>(length<kilometre, impl<int>>(1)).count() == impl<int>(1000));
|
||||||
|
static_assert(length<metre, expl<int>>(length<kilometre, expl<int>>(expl(1))).count() == expl<int>(1000));
|
||||||
|
static_assert(length<metre, impl_impl<int>>(length<kilometre, impl_impl<int>>(1)).count() == impl_impl<int>(1000));
|
||||||
static_assert(length<metre, impl_expl<int>>(length<kilometre, impl_expl<int>>(1)).count() == impl_expl<int>(1000));
|
static_assert(length<metre, impl_expl<int>>(length<kilometre, impl_expl<int>>(1)).count() == impl_expl<int>(1000));
|
||||||
|
static_assert(length<metre, expl_impl<int>>(length<kilometre, expl_impl<int>>(expl_impl(1))).count() == expl_impl<int>(1000));
|
||||||
|
static_assert(length<metre, expl_expl<int>>(length<kilometre, expl_expl<int>>(expl_expl(1))).count() == expl_expl<int>(1000));
|
||||||
|
|
||||||
|
// static_assert(length<kilometre, impl<int>>(length<metre, impl<int>>(2000)).count() == impl<int>(2)); // should not compile (truncating conversion)
|
||||||
|
static_assert(length<kilometre, impl<int>>(quantity_cast<kilometre>(length<metre, impl<int>>(2000))).count() == impl<int>(2));
|
||||||
|
// static_assert(length<kilometre, expl<int>>(length<metre, expl<int>>(expl<int>(2000))).count() == expl<int>(2)); // should not compile (truncating conversion)
|
||||||
|
static_assert(length<kilometre, expl<int>>(quantity_cast<kilometre>(length<metre, expl<int>>(expl<int>(2000)))).count() == expl<int>(2));
|
||||||
|
// static_assert(length<kilometre, impl_impl<int>>(length<metre, impl_impl<int>>(2000)).count() == impl_impl<int>(2)); // should not compile (truncating conversion)
|
||||||
|
static_assert(length<kilometre, impl_impl<int>>(quantity_cast<kilometre>(length<metre, impl_impl<int>>(2000))).count() == impl_impl<int>(2));
|
||||||
// static_assert(length<kilometre, impl_expl<int>>(length<metre, impl_expl<int>>(2000)).count() == impl_expl<int>(2)); // should not compile (truncating conversion)
|
// static_assert(length<kilometre, impl_expl<int>>(length<metre, impl_expl<int>>(2000)).count() == impl_expl<int>(2)); // should not compile (truncating conversion)
|
||||||
static_assert(length<kilometre, impl_expl<int>>(quantity_cast<kilometre>(length<metre, impl_expl<int>>(2000))).count() == impl_expl<int>(2));
|
static_assert(length<kilometre, impl_expl<int>>(quantity_cast<kilometre>(length<metre, impl_expl<int>>(2000))).count() == impl_expl<int>(2));
|
||||||
|
// static_assert(length<kilometre, expl_impl<int>>(length<metre, expl_impl<int>>(expl_impl<int>(2000))).count() == expl_impl<int>(2)); // should not compile (truncating conversion)
|
||||||
|
static_assert(length<kilometre, expl_impl<int>>(quantity_cast<kilometre>(length<metre, expl_impl<int>>(expl_impl<int>(2000)))).count() == expl_impl<int>(2));
|
||||||
|
// static_assert(length<kilometre, expl_expl<int>>(length<metre, expl_expl<int>>(expl_expl<int>(2000))).count() == expl_expl<int>(2)); // should not compile (truncating conversion)
|
||||||
|
static_assert(length<kilometre, expl_expl<int>>(quantity_cast<kilometre>(length<metre, expl_expl<int>>(expl_expl<int>(2000)))).count() == expl_expl<int>(2));
|
||||||
|
|
||||||
static_assert(length<metre, impl_impl<int>>::zero().count() == impl_impl<int>{0});
|
// static_assert(velocity<metre_per_second, impl<int>>(velocity<kilometre_per_hour, impl<int>>(72)).count() == impl<int>(20)); // should not compile (truncating conversion)
|
||||||
static_assert(length<metre, impl_impl<int>>::min().count() == impl_impl<int>{std::numeric_limits<int>::lowest()});
|
static_assert(velocity<metre_per_second, impl<int>>(quantity_cast<metre_per_second>(velocity<kilometre_per_hour, impl<int>>(72))).count() == impl<int>(20));
|
||||||
static_assert(length<metre, impl_impl<int>>::max().count() == impl_impl<int>{std::numeric_limits<int>::max()});
|
// static_assert(velocity<metre_per_second, expl<int>>(velocity<kilometre_per_hour, expl<int>>(expl(72))).count() == expl<int>(20)); // should not compile (truncating conversion)
|
||||||
static_assert(length<metre, impl_impl<double>>::zero().count() == impl_impl<double>{0.0});
|
static_assert(velocity<metre_per_second, expl<int>>(quantity_cast<metre_per_second>(velocity<kilometre_per_hour, expl<int>>(expl(72)))).count() == expl<int>(20));
|
||||||
static_assert(length<metre, impl_impl<double>>::min().count() == impl_impl<double>{std::numeric_limits<double>::lowest()});
|
// static_assert(velocity<metre_per_second, impl_impl<int>>(velocity<kilometre_per_hour, impl_impl<int>>(72)).count() == impl_impl<int>(20)); // should not compile (truncating conversion)
|
||||||
static_assert(length<metre, impl_impl<double>>::max().count() == impl_impl<double>{std::numeric_limits<double>::max()});
|
static_assert(velocity<metre_per_second, impl_impl<int>>(quantity_cast<metre_per_second>(velocity<kilometre_per_hour, impl_impl<int>>(72))).count() == impl_impl<int>(20));
|
||||||
|
// static_assert(velocity<metre_per_second, impl_expl<int>>(velocity<kilometre_per_hour, impl_expl<int>>(72)).count() == impl_expl<int>(20)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<metre_per_second, impl_expl<int>>(quantity_cast<metre_per_second>(velocity<kilometre_per_hour, impl_expl<int>>(72))).count() == impl_expl<int>(20));
|
||||||
|
// static_assert(velocity<metre_per_second, expl_impl<int>>(velocity<kilometre_per_hour, expl_impl<int>>(expl_impl(72))).count() == expl_impl<int>(20)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<metre_per_second, expl_impl<int>>(quantity_cast<metre_per_second>(velocity<kilometre_per_hour, expl_impl<int>>(expl_impl(72)))).count() == expl_impl<int>(20));
|
||||||
|
// static_assert(velocity<metre_per_second, expl_expl<int>>(velocity<kilometre_per_hour, expl_expl<int>>(expl_expl(72))).count() == expl_expl<int>(20)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<metre_per_second, expl_expl<int>>(quantity_cast<metre_per_second>(velocity<kilometre_per_hour, expl_expl<int>>(expl_expl(72)))).count() == expl_expl<int>(20));
|
||||||
|
|
||||||
|
// static_assert(velocity<kilometre_per_hour, impl<int>>(velocity<metre_per_second, impl<int>>(20)).count() == impl<int>(72)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<kilometre_per_hour, impl<int>>(quantity_cast<kilometre_per_hour>(velocity<metre_per_second, impl<int>>(20))).count() == impl<int>(72));
|
||||||
|
// static_assert(velocity<kilometre_per_hour, expl<int>>(velocity<metre_per_second, expl<int>>(expl<int>(20))).count() == expl<int>(72)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<kilometre_per_hour, expl<int>>(quantity_cast<kilometre_per_hour>(velocity<metre_per_second, expl<int>>(expl<int>(20)))).count() == expl<int>(72));
|
||||||
|
// static_assert(velocity<kilometre_per_hour, impl_impl<int>>(velocity<metre_per_second, impl_impl<int>>(20)).count() == impl_impl<int>(72)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<kilometre_per_hour, impl_impl<int>>(quantity_cast<kilometre_per_hour>(velocity<metre_per_second, impl_impl<int>>(20))).count() == impl_impl<int>(72));
|
||||||
|
// static_assert(velocity<kilometre_per_hour, impl_expl<int>>(velocity<metre_per_second, impl_expl<int>>(20)).count() == impl_expl<int>(72)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<kilometre_per_hour, impl_expl<int>>(quantity_cast<kilometre_per_hour>(velocity<metre_per_second, impl_expl<int>>(20))).count() == impl_expl<int>(72));
|
||||||
|
// static_assert(velocity<kilometre_per_hour, expl_impl<int>>(velocity<metre_per_second, expl_impl<int>>(expl_impl<int>(20))).count() == expl_impl<int>(72)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<kilometre_per_hour, expl_impl<int>>(quantity_cast<kilometre_per_hour>(velocity<metre_per_second, expl_impl<int>>(expl_impl<int>(20)))).count() == expl_impl<int>(72));
|
||||||
|
// static_assert(velocity<kilometre_per_hour, expl_expl<int>>(velocity<metre_per_second, expl_expl<int>>(expl_expl<int>(20))).count() == expl_expl<int>(72)); // should not compile (truncating conversion)
|
||||||
|
static_assert(velocity<kilometre_per_hour, expl_expl<int>>(quantity_cast<kilometre_per_hour>(velocity<metre_per_second, expl_expl<int>>(expl_expl<int>(20)))).count() == expl_expl<int>(72));
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
Reference in New Issue
Block a user