Merge pull request #585 from mpusz/reference_modifiers

Reference specifiers
This commit is contained in:
Mateusz Pusz
2024-06-21 04:18:56 +09:00
committed by GitHub
19 changed files with 245 additions and 108 deletions

View File

@ -4,6 +4,8 @@
### 2.3.0 <small>WIP</small> { id="2.3.0" }
- (!) feat: Reference specifiers [#585](https://github.com/mpusz/mp-units/pull/585)
### 2.2.0 <small>June 14, 2024</small> { id="2.2.0" }
- (!) feat: C++ modules support added by [@JohelEGP](https://github.com/JohelEGP)

View File

@ -238,7 +238,7 @@ This introduces an additional type-safety.
using namespace mp_units::si::unit_symbols;
using namespace mp_units::usc::unit_symbols;
quantity_point temp{20. * deg_C};
quantity_point temp = 20. * absolute<deg_C>;
std::println("Temperature: {} ({})",
temp.quantity_from_zero(),
temp.in(deg_F).quantity_from_zero());
@ -259,7 +259,7 @@ This introduces an additional type-safety.
using namespace mp_units::si::unit_symbols;
using namespace mp_units::usc::unit_symbols;
quantity_point temp{20. * deg_C};
quantity_point temp = 20. * absolute<deg_C>;
std::println("Temperature: {} ({})",
temp.quantity_from_zero(),
temp.in(deg_F).quantity_from_zero());

View File

@ -378,5 +378,5 @@ For example:
the previous example:
```cpp
constexpr auto room_reference_temperature = ice_point + isq::Celsius_temperature(21 * deg_C);
constexpr auto room_reference_temperature = ice_point + isq::Celsius_temperature(21 * delta<deg_C>);
```

View File

@ -3,7 +3,7 @@
The affine space has two types of entities:
- **_Point_** - a position specified with coordinate values (e.g., location, address, etc.)
- **_Displacement vectors_** - the difference between two points (e.g., shift, offset,
- **_Displacement vector_** - the difference between two points (e.g., shift, offset,
displacement, duration, etc.)
In the following subchapters, we will often refer to _displacement vectors_ simply as _vectors_ for
@ -70,6 +70,49 @@ difference between two things:
As we already know, a `quantity` type provides all operations required for a _displacement vector_
abstraction in an affine space.
Quantities are constructed from a delta quantity reference. Most of units are considered to be
delta references by default. The ones that need a special qualification are the units that
get a point origin in their definition (i.e., units of temperature).
We can create a `quantity` by passing a delta quantity reference to either:
- two-parameter constructor:
```cpp
quantity q1(42, si::metre);
// quantity q2(42, si::kelvin); // Compile-time error
// quantity q3(42, si::degree_Celsius); // Compile-time error
// quantity q4(42, usc::degree_Fahrenheit); // Compile-time error
quantity q5(42, delta<si::metre>);
quantity q6(42, delta<si::kelvin>);
quantity q7(42, delta<si::degree_Celsius>);
quantity q8(42, delta<usc::degree_Fahrenheit>);
```
- multiply syntax:
```cpp
quantity q1 = 42 * m;
// quantity q2 = 42 * K; // Compile-time error
// quantity q3 = 42 * deg_C; // Compile-time error
// quantity q4 = 42 * deg_F; // Compile-time error
quantity q5 = 42 * delta<m>;
quantity q6 = 42 * delta<K>;
quantity q7 = 42 * delta<deg_C>;
quantity q8 = 42 * delta<deg_F>;
```
!!! note
`delta` specifier is used to qualify the entire reference upon `quantity` construction.
It does not satisfy the [`Reference`](concepts.md#Reference) concept. This means that,
for example, the below are ill-formed:
```cpp
void foo(quantity<delta<si::degree_Celsius>> temp); // ill-formed
quantity<N * m / (delta<deg_C> * mol)> specific_heat_capacity; // ill-formed
quantity R = 8.314 * N * m / (delta<deg_C> * mol); // ill-formed
```
## _Point_ is modeled by `quantity_point` and `PointOrigin`
@ -109,6 +152,19 @@ scale zeroth point using the following rules:
- otherwise, an instantiation of `zeroth_point_origin<QuantitySpec>` is being used which
provides a well-established zeroth point for a specific quantity type.
Quantity points with default point origins may be constructed using multiply syntax from an
absolute quantity reference. None of units are considered to be absolute references by default,
so they need a special qualification:
```cpp
// quantity_point qp1 = 42 * m; // Compile-time error
// quantity_point qp2 = 42 * K; // Compile-time error
// quantity_point qp3 = 42 * deg_C; // Compile-time error
quantity_point qp4 = 42 * absolute<m>;
quantity_point qp5 = 42 * absolute<K>;
quantity_point qp6 = 42 * absolute<deg_C>;
```
!!! tip
The `quantity_point` definition can be found in the `mp-units/quantity_point.h` header file.
@ -124,8 +180,8 @@ for this domain.
![affine_space_1](affine_space_1.svg){style="width:80%;display: block;margin: 0 auto;"}
```cpp
quantity_point<isq::distance[si::metre]> qp1{100 * m};
quantity_point<isq::distance[si::metre]> qp2{120 * m};
quantity_point<isq::distance[si::metre]> qp1 = 100 * absolute<m>;
quantity_point<isq::distance[si::metre]> qp2 = 120 * absolute<m>;
assert(qp1.quantity_from_zero() == 100 * m);
assert(qp2.quantity_from_zero() == 120 * m);
@ -174,8 +230,8 @@ origin.
```cpp
inline constexpr struct origin final : absolute_point_origin<isq::distance> {} origin;
// quantity_point<si::metre, origin> qp1{100 * m}; // Compile-time error
// quantity_point<si::metre, origin> qp2{120 * m}; // Compile-time error
// quantity_point<si::metre, origin> qp1{100 * m}; // Compile-time error
// quantity_point<si::metre, origin> qp2 = 120 * absolute<m>; // Compile-time error
quantity_point<si::metre, origin> qp1 = origin + 100 * m;
quantity_point<si::metre, origin> qp2 = 120 * m + origin;
@ -387,7 +443,7 @@ namespace si {
inline constexpr struct absolute_zero final : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;
inline constexpr auto zeroth_kelvin = absolute_zero;
inline constexpr struct ice_point final : relative_point_origin<quantity_point{273'150 * milli<kelvin>}> {} ice_point;
inline constexpr struct ice_point final : relative_point_origin<273'150 * absolute<milli<kelvin>>>> {} ice_point;
inline constexpr auto zeroth_degree_Celsius = ice_point;
}
@ -395,7 +451,7 @@ inline constexpr auto zeroth_degree_Celsius = ice_point;
namespace usc {
inline constexpr struct zeroth_degree_Fahrenheit final :
relative_point_origin<quantity_point{-32 * (mag_ratio<5, 9> * si::degree_Celsius)}> {} zeroth_degree_Fahrenheit;
relative_point_origin<-32 * absolute<mag_ratio<5, 9> * si::degree_Celsius>> {} zeroth_degree_Fahrenheit;
}
```
@ -444,25 +500,28 @@ choose from here. Depending on our needs or tastes, we can:
- be explicit about the unit and origin:
```cpp
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q1 = si::zeroth_degree_Celsius + 20.5 * deg_C;
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q2 = {20.5 * deg_C, si::zeroth_degree_Celsius};
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q3{20.5 * deg_C};
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q1 = si::zeroth_degree_Celsius + 20.5 * delta<deg_C>;
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q2 = {20.5 * delta<deg_C>, si::zeroth_degree_Celsius};
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q3{20.5 * delta<deg_C>};
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q4 = 20.5 * absolute<deg_C>;
```
- specify a unit and use its zeroth point origin implicitly:
```cpp
quantity_point<si::degree_Celsius> q4 = si::zeroth_degree_Celsius + 20.5 * deg_C;
quantity_point<si::degree_Celsius> q5 = {20.5 * deg_C, si::zeroth_degree_Celsius};
quantity_point<si::degree_Celsius> q6{20.5 * deg_C};
quantity_point<si::degree_Celsius> q5 = si::zeroth_degree_Celsius + 20.5 * delta<deg_C>;
quantity_point<si::degree_Celsius> q6 = {20.5 * delta<deg_C>, si::zeroth_degree_Celsius};
quantity_point<si::degree_Celsius> q7{20.5 * delta<deg_C>};
quantity_point<si::degree_Celsius> q8 = 20.5 * absolute<deg_C>;
```
- benefit from CTAD:
```cpp
quantity_point q7 = si::zeroth_degree_Celsius + 20.5 * deg_C;
quantity_point q8 = {20.5 * deg_C, si::zeroth_degree_Celsius};
quantity_point q9{20.5 * deg_C};
quantity_point q9 = si::zeroth_degree_Celsius + 20.5 * delta<deg_C>;
quantity_point q10 = {20.5 * delta<deg_C>, si::zeroth_degree_Celsius};
quantity_point q11{20.5 * delta<deg_C>};
quantity_point q12 = 20.5 * absolute<deg_C>;
```
In all of the above cases, we end up with the `quantity_point` of the same type and value.
@ -473,10 +532,10 @@ the following way:
![affine_space_6](affine_space_6.svg){style="width:80%;display: block;margin: 0 auto;"}
```cpp
constexpr struct room_reference_temp final : relative_point_origin<quantity_point{21 * deg_C}> {} room_reference_temp;
constexpr struct room_reference_temp final : relative_point_origin<21 * absolute<deg_C>> {} room_reference_temp;
using room_temp = quantity_point<isq::Celsius_temperature[deg_C], room_reference_temp>;
constexpr auto step_delta = isq::Celsius_temperature(0.5 * deg_C);
constexpr auto step_delta = isq::Celsius_temperature(0.5 * delta<deg_C>);
constexpr int number_of_steps = 6;
room_temp room_ref{};

View File

@ -62,12 +62,13 @@ int main()
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
const quantity process_noise_variance = 0.0001 * pow<2>(deg_C);
const estimate initial{state{qp{60. * deg_C}}, 100. * deg_C};
const std::array measurements = {qp{49.986 * deg_C}, qp{49.963 * deg_C}, qp{50.097 * deg_C}, qp{50.001 * deg_C},
qp{50.018 * deg_C}, qp{50.05 * deg_C}, qp{49.938 * deg_C}, qp{49.858 * deg_C},
qp{49.965 * deg_C}, qp{50.114 * deg_C}};
const quantity measurement_error = 0.1 * deg_C;
const quantity process_noise_variance = 0.0001 * delta<pow<2>(deg_C)>;
const estimate initial{state{qp{60. * delta<deg_C>}}, 100. * delta<deg_C>};
const std::array measurements = {qp{49.986 * delta<deg_C>}, qp{49.963 * delta<deg_C>}, qp{50.097 * delta<deg_C>},
qp{50.001 * delta<deg_C>}, qp{50.018 * delta<deg_C>}, qp{50.05 * delta<deg_C>},
qp{49.938 * delta<deg_C>}, qp{49.858 * delta<deg_C>}, qp{49.965 * delta<deg_C>},
qp{50.114 * delta<deg_C>}};
const quantity measurement_error = 0.1 * delta<deg_C>;
const quantity measurement_variance = pow<2>(measurement_error);
auto predict = [=](const estimate& current) {

View File

@ -62,12 +62,13 @@ int main()
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
const quantity process_noise_variance = 0.0001 * pow<2>(deg_C);
const estimate initial{state{qp{10. * deg_C}}, 100. * deg_C};
const std::array measurements = {qp{50.486 * deg_C}, qp{50.963 * deg_C}, qp{51.597 * deg_C}, qp{52.001 * deg_C},
qp{52.518 * deg_C}, qp{53.05 * deg_C}, qp{53.438 * deg_C}, qp{53.858 * deg_C},
qp{54.465 * deg_C}, qp{55.114 * deg_C}};
const quantity measurement_error = 0.1 * deg_C;
const quantity process_noise_variance = 0.0001 * delta<pow<2>(deg_C)>;
const estimate initial{state{qp{10. * delta<deg_C>}}, 100. * delta<deg_C>};
const std::array measurements = {qp{50.486 * delta<deg_C>}, qp{50.963 * delta<deg_C>}, qp{51.597 * delta<deg_C>},
qp{52.001 * delta<deg_C>}, qp{52.518 * delta<deg_C>}, qp{53.05 * delta<deg_C>},
qp{53.438 * delta<deg_C>}, qp{53.858 * delta<deg_C>}, qp{54.465 * delta<deg_C>},
qp{55.114 * delta<deg_C>}};
const quantity measurement_error = 0.1 * delta<deg_C>;
const quantity measurement_variance = pow<2>(measurement_error);
auto predict = [=](const estimate& current) {

View File

@ -62,12 +62,13 @@ int main()
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
const quantity process_noise_variance = 0.15 * pow<2>(deg_C);
const estimate initial{state{qp{10. * deg_C}}, 100. * deg_C};
const std::array measurements = {qp{50.486 * deg_C}, qp{50.963 * deg_C}, qp{51.597 * deg_C}, qp{52.001 * deg_C},
qp{52.518 * deg_C}, qp{53.05 * deg_C}, qp{53.438 * deg_C}, qp{53.858 * deg_C},
qp{54.465 * deg_C}, qp{55.114 * deg_C}};
const quantity measurement_error = 0.1 * deg_C;
const quantity process_noise_variance = 0.15 * delta<pow<2>(deg_C)>;
const estimate initial{state{qp{10. * delta<deg_C>}}, 100. * delta<deg_C>};
const std::array measurements = {qp{50.486 * delta<deg_C>}, qp{50.963 * delta<deg_C>}, qp{51.597 * delta<deg_C>},
qp{52.001 * delta<deg_C>}, qp{52.518 * delta<deg_C>}, qp{53.05 * delta<deg_C>},
qp{53.438 * delta<deg_C>}, qp{53.858 * delta<deg_C>}, qp{54.465 * delta<deg_C>},
qp{55.114 * delta<deg_C>}};
const quantity measurement_error = 0.1 * delta<deg_C>;
const quantity measurement_variance = pow<2>(measurement_error);
auto predict = [=](const estimate& current) {

View File

@ -84,7 +84,7 @@ int main()
const auto t3 = std::make_tuple(isq::energy(q3 * h), isq::wavenumber(q3 / c), q3,
isq::thermodynamic_temperature(q3 * h / kb), isq::wavelength(c / q3));
const auto q4 = isq::thermodynamic_temperature(1. * K);
const auto q4 = isq::thermodynamic_temperature(1. * delta<K>);
const auto t4 = std::make_tuple(isq::energy(q4 * kb), isq::wavenumber(q4 * kb / (h * c)), isq::frequency(q4 * kb / h),
q4, isq::wavelength(h * c / (q4 * kb)));

View File

@ -97,10 +97,10 @@ template<Quantity To, typename From>
if constexpr (q_unit == To::unit) {
// no scaling of the number needed
return {static_cast<MP_UNITS_TYPENAME To::rep>(std::forward<From>(q).numerical_value_is_an_implementation_detail_),
To::reference}; // this is the only (and recommended) way to do
// a truncating conversion on a number, so we are
// using static_cast to suppress all the compiler
// warnings on conversions
delta<To::reference>}; // this is the only (and recommended) way to do
// a truncating conversion on a number, so we are
// using static_cast to suppress all the compiler
// warnings on conversions
} else {
// scale the number
using traits = magnitude_conversion_traits<To, std::remove_reference_t<From>>;
@ -108,13 +108,13 @@ template<Quantity To, typename From>
// this results in great assembly
auto res = static_cast<MP_UNITS_TYPENAME To::rep>(
static_cast<traits::c_type>(q.numerical_value_is_an_implementation_detail_) * traits::ratio);
return {res, To::reference};
return {res, delta<To::reference>};
} else {
// this is slower but allows conversions like 2000 m -> 2 km without loosing data
auto res = static_cast<MP_UNITS_TYPENAME To::rep>(
static_cast<traits::c_type>(q.numerical_value_is_an_implementation_detail_) * traits::num_mult /
traits::den_mult * traits::irr_mult);
return {res, To::reference};
return {res, delta<To::reference>};
}
}
}
@ -175,5 +175,4 @@ template<QuantityPoint ToQP, typename FromQP>
}
}
} // namespace mp_units::detail

View File

@ -88,6 +88,13 @@ template<typename Func, Quantity Q1, Quantity Q2>
using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference),
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
template<auto T, auto R>
concept SameOriginalReferenceAs = DeltaReference<MP_UNITS_REMOVE_CONST(decltype(T))> &&
Reference<MP_UNITS_REMOVE_CONST(decltype(R))> && (get_original_reference(T) == R);
template<auto R1, auto R2, typename Rep1, typename Rep2>
concept SameValueAs = detail::SameOriginalReferenceAs<R1, R2> && std::same_as<Rep1, Rep2>;
} // namespace detail
MP_UNITS_EXPORT_BEGIN
@ -117,25 +124,25 @@ public:
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<rep>::zero(); }
{
return {quantity_values<rep>::zero(), R};
return {quantity_values<rep>::zero(), delta<R>};
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<rep>::one(); }
{
return {quantity_values<rep>::one(), R};
return {quantity_values<rep>::one(), delta<R>};
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<rep>::min(); }
{
return {quantity_values<rep>::min(), R};
return {quantity_values<rep>::min(), delta<R>};
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<rep>::max(); }
{
return {quantity_values<rep>::max(), R};
return {quantity_values<rep>::max(), delta<R>};
}
// construction, assignment, destruction
@ -144,15 +151,18 @@ public:
quantity(quantity&&) = default;
~quantity() = default;
template<typename Value>
requires std::same_as<std::remove_cvref_t<Value>, Rep>
constexpr quantity(Value&& v, decltype(R)) : numerical_value_is_an_implementation_detail_(std::forward<Value>(v))
template<typename Value, DeltaReference R2>
requires detail::SameValueAs<R2{}, R, std::remove_cvref_t<Value>, Rep>
constexpr quantity(Value&& v, R2) : numerical_value_is_an_implementation_detail_(std::forward<Value>(v))
{
}
template<typename Value, Reference R2>
requires detail::QuantityConvertibleTo<quantity<R2{}, std::remove_cvref_t<Value>>, quantity>
constexpr quantity(Value&& v, R2) : quantity(quantity<R2{}, std::remove_cvref_t<Value>>{std::forward<Value>(v), R2{}})
template<typename Value, DeltaReference R2>
requires(!detail::SameValueAs<R2{}, R, std::remove_cvref_t<Value>, Rep>) &&
detail::QuantityConvertibleTo<quantity<detail::get_original_reference(R2{}), std::remove_cvref_t<Value>>,
quantity>
constexpr quantity(Value&& v, R2) :
quantity(quantity<detail::get_original_reference(R2{}), std::remove_cvref_t<Value>>{std::forward<Value>(v), R2{}})
{
}
@ -171,8 +181,8 @@ public:
convert_explicitly> ||
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
!std::convertible_to<typename quantity_like_traits<Q>::rep, Rep>) quantity(const Q& q) :
quantity(
::mp_units::quantity{quantity_like_traits<Q>::to_numerical_value(q).value, quantity_like_traits<Q>::reference})
quantity(::mp_units::quantity{quantity_like_traits<Q>::to_numerical_value(q).value,
delta<quantity_like_traits<Q>::reference>})
{
}
@ -266,7 +276,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{+numerical_value_is_an_implementation_detail_, reference};
return ::mp_units::quantity{+numerical_value_is_an_implementation_detail_, delta<reference>};
}
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator-() const
@ -276,7 +286,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, reference};
return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, delta<reference>};
}
template<typename Q>
@ -298,7 +308,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, reference};
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, delta<reference>};
}
template<typename Q>
@ -320,7 +330,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_--, reference};
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_--, delta<reference>};
}
// compound assignment operators
@ -419,9 +429,9 @@ public:
};
// CTAD
template<typename Value, Reference R>
requires RepresentationOf<Value, get_quantity_spec(R{}).character>
quantity(Value v, R) -> quantity<R{}, Value>;
template<typename Value, DeltaReference R>
requires RepresentationOf<Value, get_quantity_spec(detail::get_original_reference(R{})).character>
quantity(Value v, R) -> quantity<detail::get_original_reference(R{}), Value>;
template<QuantityLike Q>
explicit(
@ -437,7 +447,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
const ret ret_lhs(lhs);
const ret ret_rhs(rhs);
return quantity{ret_lhs.numerical_value_ref_in(ret::unit) + ret_rhs.numerical_value_ref_in(ret::unit),
ret::reference};
delta<ret::reference>};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
@ -448,7 +458,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
const ret ret_lhs(lhs);
const ret ret_rhs(rhs);
return quantity{ret_lhs.numerical_value_ref_in(ret::unit) - ret_rhs.numerical_value_ref_in(ret::unit),
ret::reference};
delta<ret::reference>};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
@ -461,14 +471,14 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
const ret ret_lhs(lhs);
const ret ret_rhs(rhs);
return quantity{ret_lhs.numerical_value_ref_in(ret::unit) % ret_rhs.numerical_value_ref_in(ret::unit),
ret::reference};
delta<ret::reference>};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::multiplies<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator*(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
return quantity{lhs.numerical_value_ref_in(get_unit(R1)) * rhs.numerical_value_ref_in(get_unit(R2)), R1 * R2};
return quantity{lhs.numerical_value_ref_in(get_unit(R1)) * rhs.numerical_value_ref_in(get_unit(R2)), delta<R1 * R2>};
}
template<auto R, typename Rep, typename Value>
@ -476,7 +486,7 @@ template<auto R, typename Rep, typename Value>
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, Rep, const Value&>
[[nodiscard]] constexpr QuantityOf<get_quantity_spec(R)> auto operator*(const quantity<R, Rep>& q, const Value& v)
{
return quantity{q.numerical_value_ref_in(get_unit(R)) * v, R};
return quantity{q.numerical_value_ref_in(get_unit(R)) * v, delta<R>};
}
template<typename Value, auto R, typename Rep>
@ -484,7 +494,7 @@ template<typename Value, auto R, typename Rep>
detail::InvokeResultOf<get_quantity_spec(R).character, std::multiplies<>, const Value&, Rep>
[[nodiscard]] constexpr QuantityOf<get_quantity_spec(R)> auto operator*(const Value& v, const quantity<R, Rep>& q)
{
return quantity{v * q.numerical_value_ref_in(get_unit(R)), R};
return quantity{v * q.numerical_value_ref_in(get_unit(R)), delta<R>};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
@ -492,7 +502,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero());
return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), R1 / R2};
return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), delta<R1 / R2>};
}
template<auto R, typename Rep, typename Value>
@ -501,7 +511,7 @@ template<auto R, typename Rep, typename Value>
[[nodiscard]] constexpr QuantityOf<get_quantity_spec(R)> auto operator/(const quantity<R, Rep>& q, const Value& v)
{
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
return quantity{q.numerical_value_ref_in(get_unit(R)) / v, R};
return quantity{q.numerical_value_ref_in(get_unit(R)) / v, delta<R>};
}
template<typename Value, auto R, typename Rep>
@ -510,7 +520,7 @@ template<typename Value, auto R, typename Rep>
[[nodiscard]] constexpr QuantityOf<inverse(get_quantity_spec(R))> auto operator/(const Value& v,
const quantity<R, Rep>& q)
{
return quantity{v / q.numerical_value_ref_in(get_unit(R)), ::mp_units::one / R};
return quantity{v / q.numerical_value_ref_in(get_unit(R)), delta<::mp_units::one / R>};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>

View File

@ -57,7 +57,7 @@ template<QuantitySpec auto ToQS, typename Q>
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
make_reference(ToQS, std::remove_reference_t<Q>::unit)};
delta<make_reference(ToQS, std::remove_reference_t<Q>::unit)>};
}
/**

View File

@ -129,7 +129,7 @@ struct quantity_spec_interface {
(explicitly_convertible(std::remove_reference_t<Q>::quantity_spec, self))
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
detail::make_reference(self, std::remove_cvref_t<Q>::unit)};
delta<detail::make_reference(self, std::remove_cvref_t<Q>::unit)>};
}
#else
template<typename Self_ = Self, UnitOf<Self_{}> U>
@ -144,7 +144,7 @@ struct quantity_spec_interface {
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit)};
delta<detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit)>};
}
#endif
};
@ -341,7 +341,7 @@ struct quantity_spec<Self, QS, Args...> : detail::propagate_equation<QS>, detail
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit)};
delta<detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit)>};
}
#endif
};

View File

@ -26,6 +26,7 @@
#include <mp-units/bits/get_associated_quantity.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/framework/quantity_concepts.h>
#include <mp-units/framework/quantity_point_concepts.h>
#include <mp-units/framework/reference_concepts.h>
#include <mp-units/framework/representation_concepts.h>
@ -40,8 +41,18 @@ namespace detail {
template<QuantitySpec auto Q, Unit auto U>
using reference_t = reference<MP_UNITS_REMOVE_CONST(decltype(Q)), MP_UNITS_REMOVE_CONST(decltype(U))>;
template<typename R>
requires DeltaReference<R> || AbsoluteReference<R>
[[nodiscard]] consteval Reference auto get_original_reference(R r)
{
if constexpr (requires { R::_original_reference_; })
return R::_original_reference_;
else
return r;
}
} // namespace detail
MP_UNITS_EXPORT_BEGIN
[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u)
@ -173,18 +184,39 @@ struct reference {
};
template<typename Rep, Reference R>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity<R{}, std::remove_cvref_t<Rep>> operator*(Rep&& lhs, R)
template<typename Rep, DeltaReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(detail::get_original_reference(R{})).character>
[[nodiscard]] constexpr quantity<detail::get_original_reference(R{}), std::remove_cvref_t<Rep>> operator*(Rep&& lhs, R)
{
return quantity{std::forward<Rep>(lhs), R{}};
}
template<typename Rep, Reference R>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity<inverse(R{}), std::remove_cvref_t<Rep>> operator/(Rep&& lhs, R)
template<typename Rep, DeltaReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(detail::get_original_reference(R{})).character>
[[nodiscard]] constexpr quantity<inverse(detail::get_original_reference(R{})), std::remove_cvref_t<Rep>> operator/(
Rep&& lhs, R)
{
return quantity{std::forward<Rep>(lhs), inverse(R{})};
return quantity{std::forward<Rep>(lhs), delta<inverse(detail::get_original_reference(R{}))>};
}
template<typename Rep, AbsoluteReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(detail::get_original_reference(R{})).character>
[[nodiscard]] constexpr quantity_point<detail::get_original_reference(R{}),
default_point_origin(detail::get_original_reference(R{})),
std::remove_cvref_t<Rep>>
operator*(Rep&& lhs, R)
{
return quantity_point{std::forward<Rep>(lhs) * delta<detail::get_original_reference(R{})>};
}
template<typename Rep, AbsoluteReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(detail::get_original_reference(R{})).character>
[[nodiscard]] constexpr quantity_point<inverse(detail::get_original_reference(R{})),
default_point_origin(detail::get_original_reference(R{})),
std::remove_cvref_t<Rep>>
operator/(Rep&& lhs, R)
{
return quantity_point{std::forward<Rep>(lhs) * delta<inverse(detail::get_original_reference(R{}))>};
}
template<Reference R, typename Rep>
@ -202,7 +234,7 @@ template<typename Q, Reference R>
[[nodiscard]] constexpr Quantity auto operator*(Q&& q, R)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
std::remove_cvref_t<Q>::reference * R{}};
delta<std::remove_cvref_t<Q>::reference * R{}>};
}
template<typename Q, Reference R>
@ -210,7 +242,7 @@ template<typename Q, Reference R>
[[nodiscard]] constexpr Quantity auto operator/(Q&& q, R)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
std::remove_cvref_t<Q>::reference / R{}};
delta<std::remove_cvref_t<Q>::reference / R{}>};
}
template<Reference R, typename Q>

View File

@ -80,4 +80,32 @@ concept ReferenceOf = Reference<T> && QuantitySpecOf<decltype(get_quantity_spec(
MP_UNITS_EXPORT_END
// reference specifiers
template<Reference R>
struct relative_ final {
static constexpr Reference auto _original_reference_ = R{};
};
template<Reference R>
struct absolute_ final {
static constexpr Reference auto _original_reference_ = R{};
};
MP_UNITS_EXPORT_BEGIN
template<Reference auto R>
inline constexpr relative_<MP_UNITS_REMOVE_CONST(decltype(R))> delta{};
template<Reference auto R>
inline constexpr absolute_<MP_UNITS_REMOVE_CONST(decltype(R))> absolute{};
template<typename T>
concept DeltaReference =
(Reference<T> && !requires { get_unit(T{}).point_origin; }) || is_specialization_of<T, relative_>;
template<typename T>
concept AbsoluteReference = is_specialization_of<T, absolute_>;
MP_UNITS_EXPORT_END
} // namespace mp_units

View File

@ -115,7 +115,7 @@ template<ReferenceOf<dimensionless> auto R, typename Rep>
{
using std::exp;
return value_cast<get_unit(R)>(
quantity{static_cast<Rep>(exp(q.force_numerical_value_in(q.unit))), detail::clone_reference_with<one>(R)});
quantity{static_cast<Rep>(exp(q.force_numerical_value_in(q.unit))), delta<detail::clone_reference_with<one>(R)>});
}
/**
@ -236,7 +236,7 @@ template<auto R, auto S, auto T, typename Rep1, typename Rep2, typename Rep3>
using std::fma;
return quantity{
fma(a.numerical_value_ref_in(a.unit), x.numerical_value_ref_in(x.unit), b.numerical_value_ref_in(b.unit)),
common_reference(R * S, T)};
delta<common_reference(R * S, T)>};
}
/**
@ -260,7 +260,7 @@ template<auto R, auto S, auto T, auto Origin, typename Rep1, typename Rep2, type
using std::fma;
return Origin + quantity{fma(a.numerical_value_ref_in(a.unit), x.numerical_value_ref_in(x.unit),
b.quantity_ref_from(b.point_origin).numerical_value_ref_in(b.unit)),
common_reference(R * S, T)};
delta<common_reference(R * S, T)>};
}
/**
@ -277,7 +277,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
constexpr auto ref = common_reference(R1, R2);
constexpr auto unit = get_unit(ref);
using std::fmod;
return quantity{fmod(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref};
return quantity{fmod(x.numerical_value_in(unit), y.numerical_value_in(unit)), delta<ref>};
}
/**
@ -294,7 +294,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
constexpr auto ref = common_reference(R1, R2);
constexpr auto unit = get_unit(ref);
using std::remainder;
return quantity{remainder(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref};
return quantity{remainder(x.numerical_value_in(unit), y.numerical_value_in(unit)), delta<ref>};
}
@ -339,7 +339,7 @@ template<Unit auto To, auto R, typename Rep>
return {static_cast<Rep>(floor(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with<To>(R)};
} else {
return handle_signed_results(
quantity{static_cast<Rep>(floor(q.force_numerical_value_in(To))), detail::clone_reference_with<To>(R)});
quantity{static_cast<Rep>(floor(q.force_numerical_value_in(To))), delta<detail::clone_reference_with<To>(R)>});
}
} else {
if constexpr (To == get_unit(R)) {
@ -376,7 +376,7 @@ template<Unit auto To, auto R, typename Rep>
return {static_cast<Rep>(ceil(q.numerical_value_ref_in(q.unit))), detail::clone_reference_with<To>(R)};
} else {
return handle_signed_results(
quantity{static_cast<Rep>(ceil(q.force_numerical_value_in(To))), detail::clone_reference_with<To>(R)});
quantity{static_cast<Rep>(ceil(q.force_numerical_value_in(To))), delta<detail::clone_reference_with<To>(R)>});
}
} else {
if constexpr (To == get_unit(R)) {
@ -456,7 +456,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
constexpr auto ref = common_reference(R1, R2);
constexpr auto unit = get_unit(ref);
using std::hypot;
return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref};
return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit)), delta<ref>};
}
/**
@ -474,7 +474,8 @@ template<auto R1, typename Rep1, auto R2, typename Rep2, auto R3, typename Rep3>
constexpr auto ref = common_reference(R1, R2);
constexpr auto unit = get_unit(ref);
using std::hypot;
return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit), z.numerical_value_in(unit)), ref};
return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit), z.numerical_value_in(unit)),
delta<ref>};
}
} // namespace mp_units

View File

@ -77,7 +77,7 @@ inline constexpr struct weber final : named_unit<"Wb", volt * second> {} weber;
inline constexpr struct tesla final : named_unit<"T", weber / square(metre)> {} tesla;
inline constexpr struct henry final : named_unit<"H", weber / ampere> {} henry;
inline constexpr struct ice_point final : relative_point_origin<quantity_point{273'150 * milli<kelvin>}> {} ice_point;
inline constexpr struct ice_point final : relative_point_origin<273'150 * absolute<milli<kelvin>>> {} ice_point;
inline constexpr auto zeroth_degree_Celsius = ice_point;
inline constexpr struct degree_Celsius final : named_unit<symbol_text{u8"°C", "`C"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;

View File

@ -118,7 +118,7 @@ inline constexpr struct troy_pound final : named_unit<"lb t", mag<12> * troy_onc
inline constexpr struct inch_of_mercury final : named_unit<"inHg", mag_ratio<3'386'389, 1'000> * si::pascal> {} inch_of_mercury;
// https://en.wikipedia.org/wiki/United_States_customary_units#Temperature
inline constexpr struct zeroth_degree_Fahrenheit final : relative_point_origin<quantity_point{-32 * (mag_ratio<5, 9> * si::degree_Celsius)}> {} zeroth_degree_Fahrenheit;
inline constexpr struct zeroth_degree_Fahrenheit final : relative_point_origin<-32 * absolute<mag_ratio<5, 9> * si::degree_Celsius>> {} zeroth_degree_Fahrenheit;
inline constexpr struct degree_Fahrenheit final : named_unit<symbol_text{u8"°F", "`F"}, mag_ratio<5, 9> * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_Fahrenheit;
// clang-format on

View File

@ -801,9 +801,12 @@ static_assert(
//////////////////////////////////
static_assert(quantity_point{42 * m}.quantity_from_zero() == 42 * m);
static_assert((42 * absolute<m>).quantity_from_zero() == 42 * m);
static_assert(quantity_point{isq::height(42 * m)}.quantity_from_zero() == 42 * m);
static_assert(quantity_point{20 * deg_C}.quantity_from_zero() == 20 * deg_C);
static_assert(quantity_point{20. * deg_C}.in(deg_F).quantity_from_zero() == 68 * deg_F);
static_assert(quantity_point{20 * delta<deg_C>}.quantity_from_zero() == 20 * delta<deg_C>);
static_assert((20 * absolute<deg_C>).quantity_from_zero() == 20 * delta<deg_C>);
static_assert(quantity_point{20. * delta<deg_C>}.in(deg_F).quantity_from_zero() == 68 * delta<deg_F>);
static_assert((20. * absolute<deg_C>).in(deg_F).quantity_from_zero() == 68 * delta<deg_F>);
static_assert((mean_sea_level + 42 * m).quantity_from_zero() == 42 * m);
static_assert((ground_level + 42 * m).quantity_from_zero() == 84 * m);
@ -889,13 +892,12 @@ static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{isq::he
static_assert(quantity_point{isq::height(123 * m)}.unit == si::metre);
static_assert(quantity_point{isq::height(123 * m)}.quantity_spec == isq::height);
static_assert(std::is_same_v<decltype(quantity_point{20 * deg_C})::rep, int>);
static_assert(
std::is_same_v<std::remove_const_t<decltype(quantity_point{20 * deg_C}.point_origin)>, struct si::ice_point>);
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{20 * deg_C}.absolute_point_origin)>,
static_assert(std::is_same_v<decltype(20 * absolute<deg_C>)::rep, int>);
static_assert(std::is_same_v<std::remove_const_t<decltype((20 * absolute<deg_C>).point_origin)>, struct si::ice_point>);
static_assert(std::is_same_v<std::remove_const_t<decltype((20 * absolute<deg_C>).absolute_point_origin)>,
struct si::absolute_zero>);
static_assert(quantity_point{20 * deg_C}.unit == si::degree_Celsius);
static_assert(quantity_point{20 * deg_C}.quantity_spec == kind_of<isq::thermodynamic_temperature>);
static_assert((20 * absolute<deg_C>).unit == si::degree_Celsius);
static_assert((20 * absolute<deg_C>).quantity_spec == kind_of<isq::thermodynamic_temperature>);
#if MP_UNITS_HOSTED
using namespace std::chrono_literals;

View File

@ -126,6 +126,7 @@ static_assert(isq::mass(1 * lb_t) == isq::mass(12 * oz_t));
static_assert(isq::pressure(1'000 * inHg) == isq::pressure(3'386'389 * si::pascal));
// Temperature
static_assert(isq::thermodynamic_temperature(9 * deg_F) == isq::thermodynamic_temperature(5 * si::degree_Celsius));
static_assert(isq::thermodynamic_temperature(9 * delta<deg_F>) ==
isq::thermodynamic_temperature(5 * delta<si::degree_Celsius>));
} // namespace