forked from mpusz/mp-units
Overconstrained quantity operations relaxed
This commit is contained in:
@@ -3,6 +3,38 @@
|
||||
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
|
||||
-----------------------------------------------------------
|
||||
|
||||
@@ -49,7 +81,10 @@ from a regular quantity value::
|
||||
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:
|
||||
|
||||
.. code-block::
|
||||
@@ -89,14 +124,21 @@ representation types with::
|
||||
si::length<si::metre, int> d3(quantity_cast<int>(d_expl)); // OK
|
||||
|
||||
|
||||
Tricky cases
|
||||
------------
|
||||
|
||||
|
||||
|
||||
Customization points
|
||||
--------------------
|
||||
|
||||
treat_as_floating_point
|
||||
|
||||
quantity_value
|
||||
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
For more examples of custom representation types usage please refer to
|
||||
:ref:`Linear Algebra of Quantities` chapter and :ref:`measurement` example.
|
||||
For more examples of custom representation types usage please refer to :ref:`measurement`
|
||||
example.
|
||||
|
@@ -31,17 +31,6 @@
|
||||
|
||||
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
|
||||
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>`.
|
||||
*/
|
||||
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
|
||||
|
@@ -228,8 +228,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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>>;
|
||||
return cq(lhs).count() <=> cq(rhs).count();
|
||||
@@ -238,8 +237,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr auto operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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>>;
|
||||
return cq(lhs).count() == cq(rhs).count();
|
||||
@@ -250,8 +248,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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>>;
|
||||
return cq(lhs).count() == cq(rhs).count();
|
||||
@@ -260,8 +257,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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);
|
||||
}
|
||||
@@ -269,8 +265,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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>>;
|
||||
return cq(lhs).count() < cq(rhs).count();
|
||||
@@ -279,8 +274,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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);
|
||||
}
|
||||
@@ -288,8 +282,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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;
|
||||
}
|
||||
@@ -297,8 +290,7 @@ public:
|
||||
template<typename D2, typename U2, typename Rep2>
|
||||
[[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
|
||||
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);
|
||||
}
|
||||
@@ -314,7 +306,7 @@ public:
|
||||
|
||||
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)
|
||||
requires detail::basic_arithmetic<Rep1, Rep2>
|
||||
requires std::regular_invocable<std::plus<>, Rep1, Rep2>
|
||||
{
|
||||
using common_rep = decltype(lhs.count() + rhs.count());
|
||||
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>
|
||||
[[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 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>
|
||||
[[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 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>
|
||||
[[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;
|
||||
}
|
||||
|
||||
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)
|
||||
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 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>
|
||||
[[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 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>
|
||||
[[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);
|
||||
|
||||
@@ -389,7 +383,7 @@ template<Scalar Value, typename D, typename U, typename Rep>
|
||||
|
||||
template<typename D, typename U, typename Rep, Scalar Value>
|
||||
[[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});
|
||||
|
||||
@@ -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>
|
||||
[[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);
|
||||
|
||||
@@ -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>
|
||||
[[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);
|
||||
|
||||
@@ -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)
|
||||
requires (!treat_as_floating_point<Rep>) &&
|
||||
(!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 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)
|
||||
requires (!treat_as_floating_point<Rep1>) &&
|
||||
(!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 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>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
requires QuantityOf<To, D> &&
|
||||
detail::basic_arithmetic<std::common_type_t<typename To::rep, Rep, intmax_t>>
|
||||
requires QuantityOf<To, D>
|
||||
{
|
||||
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 = 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>;
|
||||
@@ -226,7 +225,6 @@ template<Unit ToU, 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)
|
||||
requires detail::basic_arithmetic<std::common_type_t<ToRep, Rep, intmax_t>>
|
||||
{
|
||||
return quantity_cast<quantity<D, U, ToRep>>(q);
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
add_library(unit_tests_static
|
||||
cgs_test.cpp
|
||||
custom_rep_test.cpp
|
||||
custom_rep_test_min_req.cpp
|
||||
custom_unit_test.cpp
|
||||
data_test.cpp
|
||||
dimension_op_test.cpp
|
||||
|
@@ -32,37 +32,48 @@ using namespace units;
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
struct arithmetic_ops {
|
||||
// constexpr T& operator+=(T other) { value_ += other.value_; return *this; }
|
||||
// constexpr T& operator-=(T other) { value_ -= other.value_; return *this; }
|
||||
// constexpr T& operator*=(T other) { value_ *= other.value_; return *this; }
|
||||
// constexpr T& operator/=(T other) { value_ /= other.value_; return *this; }
|
||||
struct equality_ops {
|
||||
[[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]] constexpr T operator-() const { return T(-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) {
|
||||
return T(lhs.value_ - rhs.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 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>
|
||||
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_{};
|
||||
impl_constructible_impl_convertible() = default;
|
||||
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>>);
|
||||
|
||||
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_{};
|
||||
expl_constructible_impl_convertible() = default;
|
||||
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>>);
|
||||
|
||||
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_{};
|
||||
impl_constructible_expl_convertible() = default;
|
||||
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>>);
|
||||
|
||||
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_{};
|
||||
expl_constructible_expl_convertible() = default;
|
||||
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 {
|
||||
|
||||
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>
|
||||
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>;
|
||||
|
||||
template<typename T>
|
||||
struct quantity_values<impl_impl<T>> {
|
||||
static constexpr impl_impl<T> zero() { return impl_impl<T>(0); }
|
||||
static constexpr impl_impl<T> max() { return std::numeric_limits<T>::max(); }
|
||||
static constexpr impl_impl<T> min() { return std::numeric_limits<T>::lowest(); }
|
||||
struct quantity_values<impl<T>> {
|
||||
static constexpr impl<T> zero() { return 0; }
|
||||
static constexpr impl<T> max() { return std::numeric_limits<T>::max(); }
|
||||
static constexpr impl<T> min() { return std::numeric_limits<T>::lowest(); }
|
||||
};
|
||||
|
||||
} // 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 {
|
||||
|
||||
using namespace units::si;
|
||||
@@ -225,15 +229,50 @@ static_assert(length<metre, impl_expl<double>>(length<metre, int>(1)).count() ==
|
||||
|
||||
// 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, 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>>(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(length<metre, impl_impl<int>>::min().count() == impl_impl<int>{std::numeric_limits<int>::lowest()});
|
||||
static_assert(length<metre, impl_impl<int>>::max().count() == impl_impl<int>{std::numeric_limits<int>::max()});
|
||||
static_assert(length<metre, impl_impl<double>>::zero().count() == impl_impl<double>{0.0});
|
||||
static_assert(length<metre, impl_impl<double>>::min().count() == impl_impl<double>{std::numeric_limits<double>::lowest()});
|
||||
static_assert(length<metre, impl_impl<double>>::max().count() == impl_impl<double>{std::numeric_limits<double>::max()});
|
||||
// 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(velocity<metre_per_second, impl<int>>(quantity_cast<metre_per_second>(velocity<kilometre_per_hour, impl<int>>(72))).count() == impl<int>(20));
|
||||
// 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(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(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(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
|
Reference in New Issue
Block a user