feat: 💥 delta and absolute construction helpers

This commit is contained in:
Mateusz Pusz
2024-06-30 12:03:24 +02:00
parent ad3035eafe
commit ba8681f90b
24 changed files with 309 additions and 330 deletions

View File

@ -4,7 +4,7 @@
### 2.3.0 <small>WIP</small> { id="2.3.0" }
- (!) feat: Reference specifiers [#585](https://github.com/mpusz/mp-units/pull/585)
- (!) feat: `delta` and `absolute` construction helpers
### 2.2.0 <small>June 14, 2024</small> { id="2.2.0" }

View File

@ -37,7 +37,7 @@ std::cout << Pressure << "\n";
The problem is related to the accidental usage of a `quantity` rather than `quantity_point` for
`Temperature`. This means that after conversion to kelvins, we will get `28 K` instead of
the expected `301.15 K`, which will corrupt all further calculations.
the expected `301.15 K`, corrupting all further calculations.
A correct code should use a `quantity_point`:
@ -48,51 +48,68 @@ quantity_point Temperature(28.0 * deg_C);
This might be an obvious thing for domain experts, but new users of the library may not be aware
of the affine space abstractions and how they influence temperature handling.
After a lengthy discussion on handling such scenarios, we decided to aid the `quantity` and
`quantity_point` construction with `absolute<Reference>` and `delta<Reference>` quantity reference
specifiers. This applies to:
After a lengthy discussion on handling such scenarios, we decided to:
- the multiply syntax,
- 2-parameter `quantity` constructor.
- make the above code ill-formed,
- provide an alternative way to create a `quantity` with the `delta` quantity construction helper.
Here are the main points of this new design:
1. All references/units that do not specify point origin (are not offset units) in their definition
are considered `delta` by default. This means that `42 * m` creates a `quantity` and is
the same as calling `42 * delta<m>`.
2. Multiply syntax is extended to allow `quantity_point` creation with the `42 * absolute<m>`
syntax. This will provide an implicit zeroth point origin.
3. For units that specify a point origin (`si::kelvin`, `si::degree_Celsius`, and
   `usc::degree_Fahrenheit`), the user always needs to specify a modifier. This means that:
- `4 * deg_C` does not compile,
- `4 * delta<deg_C>` creates a `quantity`.
- `4 * absolute<deg_C>` creates a `quantity_point`.
4. The 2-parameter `quantity` constructor requires the same:
1. All references/units that specify point origin in their definition (i.e., `si::kelvin`,
   `si::degree_Celsius`, and `usc::degree_Fahrenheit`) are excluded from the multiply syntax
(:boom: **breaking change** :boom:).
2. A new `delta` quantity construction helper is introduced:
```cpp
quantity q1(4, m); // OK
quantity q2(4, delta<m>); // OK
quantity q3(4, absolute<m>); // Compile-time error
quantity q4(4, deg_C); // Compile-time error
quantity q5(4, delta<deg_C>); // OK
quantity q6(4, absolute<deg_C>); // Compile-time error
```
- `delta<m>(42)` results with a `quantity<si::metre, int>`,
- `delta<deg_C>(5)` results with a `quantity<si::deg_C, int>`.
The `delta` and `absolute` modifiers are stripped upon construction, so the resulting `quantity`
and `quantity_point` types use the underlying unit in its type.
3. A new `absolute` quantity point construction helper is introduced:
With such changes, the offending code will not compile, forcing the user to think more about what
is written. To enable the compilation, the user has to type one of the following:
- `absolute<m>(42)` results with a `quantity_point<si::metre, zeroth_point_origin<kind_of<isq::length>>{}, int>`,
- `absolute<deg_C>(5)` results with a `quantity<si::metre, si::ice_point, int>`.
- `quantity_point Temperature(28.0 * delta<deg_C>);`
- `quantity_point Temperature = 28.0 * absolute<deg_C>;`
!!! info
If the user still insists on using `quantity` instead of a `quantity_point`, the code will
have to be written in the following way to compile successfully:
Please note that `si::kelvin` is also excluded from the multiply syntax to prevent the
following surprising issues:
```cpp
quantity Temperature = 28.0 * delta<deg_C>;
```
=== "Now"
This will yield an invalid result, but now, hopefully, it is clearly readable in the code what is
wrong here.
```cpp
quantity q = delta<K>(300);
quantity_point qp = absolute<K>(300);
static_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());
```
=== "Before"
```cpp
quantity q(300 * K);
quantity_point qp(300 * K);
static_assert(q.in(deg_C) != qp.in(deg_C).quantity_from_zero());
```
We believe that the code enforced with new utilities makes it much easier to understand what
happens here.
With such changes to the interface design, the offending code will not compile as initially written.
Users will be forced to think more about what they write. To enable the compilation, the users have
to explicitly create a:
- `quantity_point` (the intended abstraction in this example) with any of the below syntaxes:
```cpp
quantity_point Temperature = absolute<deg_C>(28.0);
auto Temperature = absolute<deg_C>(28.0);
quantity_point Temperature(delta<deg_C>(28.0));
```
- `quantity` (an incorrect abstraction in this example) with:
```cpp
quantity Temperature = delta<deg_C>(28.0);
auto Temperature = delta<deg_C>(28.0);
```
Thanks to the new design, we can immediately see what happens here and why the result might be
incorrect in the second case.

View File

@ -43,28 +43,51 @@ a number with a predefined unit:
!!! info
In case someone doesn't like the multiply syntax or there is an ambiguity between `operator*`
provided by this and other libraries, a quantity can also be created with a two-parameter
constructor:
provided by this and other libraries, there are two other ways to create a quantity:
=== "C++ modules"
1. `delta` construction helper:
```cpp
import mp_units;
=== "C++ modules"
using namespace mp_units;
```cpp
import mp_units;
quantity q{42, si::metre / si::second};
```
using namespace mp_units;
=== "Header files"
quantity q = delta<si::metre / si::second>(42);
```
```cpp
#include <mp-units/systems/si.h>
=== "Header files"
using namespace mp_units;
```cpp
#include <mp-units/systems/si.h>
quantity q{42, si::metre / si::second};
```
using namespace mp_units;
quantity q = delta<si::metre / si::second>(42);
```
2. A two-parameter constructor:
=== "C++ modules"
```cpp
import mp_units;
using namespace mp_units;
quantity q{42, si::metre / si::second};
```
=== "Header files"
```cpp
#include <mp-units/systems/si.h>
using namespace mp_units;
quantity q{42, si::metre / si::second};
```
The above creates an instance of `quantity<derived_unit<si::metre, per<si::second>>{}, int>`.
The same can be obtained using optional unit symbols:
@ -238,7 +261,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. * absolute<deg_C>;
quantity_point temp = absolute<deg_C>(20.);
std::println("Temperature: {} ({})",
temp.quantity_from_zero(),
temp.in(deg_F).quantity_from_zero());
@ -259,7 +282,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. * absolute<deg_C>;
quantity_point temp = absolute<deg_C>(20.);
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 * delta<deg_C>);
constexpr auto room_reference_temperature = ice_point + delta<isq::Celsius_temperature[deg_C]>(21);
```

View File

@ -68,51 +68,17 @@ difference between two things:
- the difference in _speed_ (even if relative to zero).
As we already know, a `quantity` type provides all operations required for a _displacement vector_
abstraction in an affine space.
abstraction in the affine space. It can be constructed with:
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>;
```
- the multiply syntax (works for most of the units),
- `delta<Reference>` construction helper (e.g., `delta<isq::height[m]>(42)`, `delta<deg_C>(3)`),
- two-parameter constructor taking a number and a quantity reference/unit.
!!! 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:
The multiply syntax support is disabled for units that provide a point origin in their
definition (i.e., units of temperature like `K`, `deg_C`, and `deg_F`).
```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`
@ -152,17 +118,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:
Quantity points with default point origins may be constructed with the `absolute` construction
helper or forcing an explicit conversion from the `quantity`:
```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>;
// quantity_point qp1 = 42 * m; // Compile-time error
// quantity_point qp2 = 42 * K; // Compile-time error
// quantity_point qp3 = delta<deg_C>(42); // Compile-time error
quantity_point qp4(42 * m);
quantity_point qp5(42 * K);
quantity_point qp6(delta<deg_C>(42));
quantity_point qp7 = absolute<m>(42);
quantity_point qp8 = absolute<K>(42);
quantity_point qp9 = absolute<deg_C>(42);
```
!!! tip
@ -180,8 +148,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 * absolute<m>;
quantity_point<isq::distance[si::metre]> qp2 = 120 * absolute<m>;
quantity_point<isq::distance[si::metre]> qp1(100 * m);
quantity_point<isq::distance[si::metre]> qp2 = absolute<m>(120);
assert(qp1.quantity_from_zero() == 100 * m);
assert(qp2.quantity_from_zero() == 120 * m);
@ -210,7 +178,7 @@ compatible:
```cpp
quantity_point<si::metre> qp1{isq::distance(100 * m)};
quantity_point<si::metre> qp2{isq::height(120 * m)};
quantity_point<si::metre> qp2 = absolute<isq::height[m]>(120);
assert(qp2.quantity_from(qp1) == 20 * m);
assert(qp1.quantity_from(qp2) == -20 * m);
@ -230,8 +198,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 * absolute<m>; // Compile-time error
// quantity_point<si::metre, origin> qp1{100 * m}; // Compile-time error
// quantity_point<si::metre, origin> qp2{delta<m>(120)}; // Compile-time error
quantity_point<si::metre, origin> qp1 = origin + 100 * m;
quantity_point<si::metre, origin> qp2 = 120 * m + origin;
@ -443,7 +411,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<273'150 * absolute<milli<kelvin>>>> {} ice_point;
inline constexpr struct ice_point final : relative_point_origin<absolute<milli<kelvin>>(273'150)}> {} ice_point;
inline constexpr auto zeroth_degree_Celsius = ice_point;
}
@ -451,7 +419,7 @@ inline constexpr auto zeroth_degree_Celsius = ice_point;
namespace usc {
inline constexpr struct zeroth_degree_Fahrenheit final :
relative_point_origin<-32 * absolute<mag_ratio<5, 9> * si::degree_Celsius>> {} zeroth_degree_Fahrenheit;
relative_point_origin<absolute<mag_ratio<5, 9> * si::degree_Celsius>(-32)> {} zeroth_degree_Fahrenheit;
}
```
@ -500,28 +468,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 * 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>;
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q1 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q2 = {delta<deg_C>(20.5), si::zeroth_degree_Celsius};
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q3{delta<deg_C>(20.5)};
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q4 = absolute<deg_C>(20.5);
```
- specify a unit and use its zeroth point origin implicitly:
```cpp
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>;
quantity_point<si::degree_Celsius> q5 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);
quantity_point<si::degree_Celsius> q6 = {delta<deg_C>(20.5), si::zeroth_degree_Celsius};
quantity_point<si::degree_Celsius> q7{delta<deg_C>(20.5)};
quantity_point<si::degree_Celsius> q8 = absolute<deg_C>(20.5);
```
- benefit from CTAD:
```cpp
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>;
quantity_point q9 = si::zeroth_degree_Celsius + delta<deg_C>(20.5);
quantity_point q10 = {delta<deg_C>(20.5), si::zeroth_degree_Celsius};
quantity_point q11{delta<deg_C>(20.5)};
quantity_point q12 = absolute<deg_C>(20.5);
```
In all of the above cases, we end up with the `quantity_point` of the same type and value.
@ -532,10 +500,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<21 * absolute<deg_C>> {} room_reference_temp;
constexpr struct room_reference_temp final : relative_point_origin<absolute<deg_C>(21)> {} 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 * delta<deg_C>);
constexpr auto step_delta = delta<isq::Celsius_temperature<deg_C>>(0.5);
constexpr int number_of_steps = 6;
room_temp room_ref{};

View File

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

View File

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

View File

@ -62,13 +62,13 @@ int main()
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
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 process_noise_variance = delta<pow<2>(deg_C)>(0.15);
const estimate initial{state{qp{delta<deg_C>(10.)}}, delta<deg_C>(100.)};
const std::array measurements = {qp{delta<deg_C>(50.486)}, qp{delta<deg_C>(50.963)}, qp{delta<deg_C>(51.597)},
qp{delta<deg_C>(52.001)}, qp{delta<deg_C>(52.518)}, qp{delta<deg_C>(53.05)},
qp{delta<deg_C>(53.438)}, qp{delta<deg_C>(53.858)}, qp{delta<deg_C>(54.465)},
qp{delta<deg_C>(55.114)}};
const quantity measurement_error = delta<deg_C>(0.1);
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. * delta<K>);
const auto q4 = delta<isq::thermodynamic_temperature[K]>(1.);
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

@ -50,6 +50,7 @@ add_mp_units_module(
include/mp-units/ext/type_name.h
include/mp-units/ext/type_traits.h
include/mp-units/framework/compare.h
include/mp-units/framework/construction_helpers.h
include/mp-units/framework/customization_points.h
include/mp-units/framework/dimension.h
include/mp-units/framework/dimension_concepts.h

View File

@ -97,10 +97,8 @@ 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_),
make_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
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 +106,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, make_delta(To::reference)};
return {res, 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, make_delta(To::reference)};
return {res, To::reference};
}
}
}

View File

@ -24,6 +24,7 @@
// IWYU pragma: begin_exports
#include <mp-units/framework/compare.h>
#include <mp-units/framework/construction_helpers.h>
#include <mp-units/framework/customization_points.h>
#include <mp-units/framework/dimension.h>
#include <mp-units/framework/dimension_concepts.h>

View File

@ -0,0 +1,67 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/framework/quantity.h>
#include <mp-units/framework/quantity_point.h>
#include <mp-units/framework/reference_concepts.h>
#include <mp-units/framework/representation_concepts.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <type_traits>
#endif
namespace mp_units {
template<Reference R>
struct delta_ {
template<typename Rep>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity<R{}, std::remove_cvref_t<Rep>> operator()(Rep&& lhs) const
{
return quantity{std::forward<Rep>(lhs), R{}};
}
};
template<Reference R>
struct absolute_ {
template<typename Rep>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity_point<R{}, default_point_origin(R{}), std::remove_cvref_t<Rep>> operator()(
Rep&& lhs) const
{
return quantity_point{quantity{std::forward<Rep>(lhs), R{}}};
}
};
MP_UNITS_EXPORT_BEGIN
template<Reference auto R>
inline constexpr delta_<MP_UNITS_REMOVE_CONST(decltype(R))> delta{};
template<Reference auto R>
inline constexpr absolute_<MP_UNITS_REMOVE_CONST(decltype(R))> absolute{};
MP_UNITS_EXPORT_END
} // namespace mp_units

View File

@ -96,13 +96,8 @@ 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))> &&
detail::SameReference<remove_reference_specifier(T), R>;
template<auto R1, auto R2, typename Rep1, typename Rep2>
concept SameValueAs = detail::SameOriginalReferenceAs<R1, R2> && std::same_as<Rep1, Rep2>;
concept SameValueAs = detail::SameReference<R1, R2> && std::same_as<Rep1, Rep2>;
} // namespace detail
@ -133,25 +128,25 @@ public:
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<rep>::zero(); }
{
return {quantity_values<rep>::zero(), detail::make_delta(R)};
return {quantity_values<rep>::zero(), R};
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<rep>::one(); }
{
return {quantity_values<rep>::one(), detail::make_delta(R)};
return {quantity_values<rep>::one(), R};
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<rep>::min(); }
{
return {quantity_values<rep>::min(), detail::make_delta(R)};
return {quantity_values<rep>::min(), R};
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<rep>::max(); }
{
return {quantity_values<rep>::max(), detail::make_delta(R)};
return {quantity_values<rep>::max(), R};
}
// construction, assignment, destruction
@ -160,7 +155,7 @@ public:
quantity(quantity&&) = default;
~quantity() = default;
template<typename Value, DeltaReference R2>
template<typename Value, Reference 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))
{
@ -173,24 +168,6 @@ public:
{
}
template<typename Value, DeltaReference R2>
requires(!Reference<R2>) && (!detail::SameValueAs<R2{}, R, std::remove_cvref_t<Value>, Rep>) &&
detail::QuantityConvertibleTo<
quantity<detail::remove_reference_specifier(R2{}), std::remove_cvref_t<Value>>, quantity>
constexpr quantity(Value&& v, R2) :
quantity(
quantity<detail::remove_reference_specifier(R2{}), std::remove_cvref_t<Value>>{std::forward<Value>(v), R2{}})
{
}
template<typename Value, Reference R2>
requires(!DeltaReference<R2>)
constexpr quantity(Value&&, R2)
{
static_assert(DeltaReference<R2>,
"References using offset units (e.g., temperatures) must be explicitly qualified with `delta`");
}
template<detail::QuantityConvertibleTo<quantity> Q>
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(!std::convertible_to<typename Q::rep, Rep>) quantity(const Q& q) :
@ -206,8 +183,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,
detail::make_delta(quantity_like_traits<Q>::reference)})
quantity(
::mp_units::quantity{quantity_like_traits<Q>::to_numerical_value(q).value, quantity_like_traits<Q>::reference})
{
}
@ -306,7 +283,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{+numerical_value_is_an_implementation_detail_, detail::make_delta(reference)};
return ::mp_units::quantity{+numerical_value_is_an_implementation_detail_, reference};
}
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator-() const
@ -316,7 +293,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, detail::make_delta(reference)};
return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, reference};
}
template<typename Q>
@ -338,7 +315,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, detail::make_delta(reference)};
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, reference};
}
template<typename Q>
@ -360,7 +337,7 @@ public:
} -> std::common_with<rep>;
}
{
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_--, detail::make_delta(reference)};
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_--, reference};
}
// compound assignment operators
@ -463,16 +440,6 @@ 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(!Reference<R>) &&
RepresentationOf<Value, get_quantity_spec(detail::remove_reference_specifier(R{})).character>
quantity(Value v, R) -> quantity<detail::remove_reference_specifier(R{}), Value>;
// the below is needed only to fire static_asserts in the constructor
template<typename Value, Reference R>
requires(!DeltaReference<R>) && RepresentationOf<Value, get_quantity_spec(R{}).character>
quantity(Value v, R) -> quantity<R{}, Value>;
template<QuantityLike Q>
explicit(
is_specialization_of<decltype(quantity_like_traits<Q>::to_numerical_value(std::declval<Q>())), convert_explicitly>)
@ -487,7 +454,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),
detail::make_delta(ret::reference)};
ret::reference};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
@ -498,7 +465,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),
detail::make_delta(ret::reference)};
ret::reference};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
@ -511,15 +478,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),
detail::make_delta(ret::reference)};
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)),
detail::make_delta(R1 * R2)};
return quantity{lhs.numerical_value_ref_in(get_unit(R1)) * rhs.numerical_value_ref_in(get_unit(R2)), R1 * R2};
}
template<auto R, typename Rep, typename Value>
@ -527,7 +493,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, detail::make_delta(R)};
return quantity{q.numerical_value_ref_in(get_unit(R)) * v, R};
}
template<typename Value, auto R, typename Rep>
@ -535,7 +501,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)), detail::make_delta(R)};
return quantity{v * q.numerical_value_ref_in(get_unit(R)), R};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>
@ -543,8 +509,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)),
detail::make_delta(R1 / R2)};
return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), R1 / R2};
}
template<auto R, typename Rep, typename Value>
@ -553,7 +518,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, detail::make_delta(R)};
return quantity{q.numerical_value_ref_in(get_unit(R)) / v, R};
}
template<typename Value, auto R, typename Rep>
@ -562,7 +527,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)), detail::make_delta(::mp_units::one / R)};
return quantity{v / q.numerical_value_ref_in(get_unit(R)), ::mp_units::one / R};
}
template<auto R1, typename Rep1, auto R2, typename Rep2>

View File

@ -58,7 +58,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_,
detail::make_delta(make_reference(ToQS, std::remove_reference_t<Q>::unit))};
make_reference(ToQS, std::remove_reference_t<Q>::unit)};
}
/**

View File

@ -129,7 +129,7 @@ struct quantity_spec_interface {
[[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
detail::make_delta(detail::make_reference(self, std::remove_cvref_t<Q>::unit))};
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_delta(detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit))};
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_delta(detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit))};
detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit)};
}
#endif
};

View File

@ -41,16 +41,6 @@ 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 remove_reference_specifier(R r)
{
if constexpr (requires { R::_original_reference_; })
return R::_original_reference_;
else
return r;
}
} // namespace detail
MP_UNITS_EXPORT_BEGIN
@ -184,62 +174,40 @@ struct reference {
};
template<typename Rep, DeltaReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>,
get_quantity_spec(detail::remove_reference_specifier(R{})).character>
[[nodiscard]] constexpr quantity<detail::remove_reference_specifier(R{}), std::remove_cvref_t<Rep>> operator*(Rep&& lhs,
R)
template<typename Rep, Reference R>
requires(!detail::OffsetUnit<decltype(get_unit(R{}))>) &&
RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity<R{}, std::remove_cvref_t<Rep>> operator*(Rep&& lhs, R)
{
return quantity{std::forward<Rep>(lhs), R{}};
}
template<typename Rep, DeltaReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>,
get_quantity_spec(detail::remove_reference_specifier(R{})).character>
[[nodiscard]] constexpr quantity<inverse(detail::remove_reference_specifier(R{})), std::remove_cvref_t<Rep>> operator/(
Rep&& lhs, R)
template<typename Rep, Reference R>
requires(!detail::OffsetUnit<decltype(get_unit(R{}))>) &&
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)
{
return quantity{std::forward<Rep>(lhs), detail::make_delta(inverse(detail::remove_reference_specifier(R{})))};
}
template<typename Rep, AbsoluteReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>,
get_quantity_spec(detail::remove_reference_specifier(R{})).character>
[[nodiscard]] constexpr quantity_point<detail::remove_reference_specifier(R{}),
default_point_origin(detail::remove_reference_specifier(R{})),
std::remove_cvref_t<Rep>>
operator*(Rep&& lhs, R)
{
return quantity_point{std::forward<Rep>(lhs) * detail::make_delta(detail::remove_reference_specifier(R{}))};
}
template<typename Rep, AbsoluteReference R>
requires RepresentationOf<std::remove_cvref_t<Rep>,
get_quantity_spec(detail::remove_reference_specifier(R{})).character>
[[nodiscard]] constexpr quantity_point<inverse(detail::remove_reference_specifier(R{})),
default_point_origin(detail::remove_reference_specifier(R{})),
std::remove_cvref_t<Rep>>
operator/(Rep&& lhs, R)
{
return quantity_point{std::forward<Rep>(lhs) * detail::make_delta(inverse(detail::remove_reference_specifier(R{})))};
return quantity{std::forward<Rep>(lhs), inverse(R{})};
}
template<typename Rep, Reference R>
requires(!DeltaReference<R>) && RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
requires detail::OffsetUnit<decltype(get_unit(R{}))> &&
RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[noreturn]] constexpr auto operator*(Rep&&, R)
{
static_assert(
DeltaReference<R>,
"References using offset units (e.g., temperatures) must be explicitly qualified with `delta` or `absolute`");
static_assert(!detail::OffsetUnit<decltype(get_unit(R{}))>,
"References using offset units (e.g., temperatures) may be constructed only with the `delta` or "
"`absolute` helpers");
}
template<typename Rep, Reference R>
requires(!DeltaReference<R>) && RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
requires detail::OffsetUnit<decltype(get_unit(R{}))> &&
RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[noreturn]] constexpr auto operator/(Rep&&, R)
{
static_assert(
DeltaReference<R>,
"References using offset units (e.g., temperatures) must be explicitly qualified with `delta` or `absolute`");
static_assert(!detail::OffsetUnit<decltype(get_unit(R{}))>,
"References using offset units (e.g., temperatures) may be constructed only with the `delta` or "
"`absolute` helpers");
}
template<Reference R, typename Rep>
@ -267,7 +235,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_,
detail::make_delta(std::remove_cvref_t<Q>::reference * R{})};
std::remove_cvref_t<Q>::reference * R{}};
}
template<typename Q, Reference R>
@ -275,7 +243,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_,
detail::make_delta(std::remove_cvref_t<Q>::reference / R{})};
std::remove_cvref_t<Q>::reference / R{}};
}
template<Reference R, typename Q>

View File

@ -80,44 +80,8 @@ concept ReferenceOf = Reference<T> && QuantitySpecOf<decltype(get_quantity_spec(
MP_UNITS_EXPORT_END
// reference specifiers
template<Reference R>
struct delta_ 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 delta_<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, delta_>;
template<typename T>
concept AbsoluteReference = is_specialization_of<T, absolute_>;
MP_UNITS_EXPORT_END
namespace detail {
template<Reference R>
[[nodiscard]] consteval DeltaReference auto make_delta(R r)
{
if constexpr (requires { get_unit(R{}).point_origin; })
return delta<R{}>;
else
return r;
}
template<auto R1, auto R2>
concept SameReference =
Reference<MP_UNITS_REMOVE_CONST(decltype(R1))> && Reference<MP_UNITS_REMOVE_CONST(decltype(R2))> && (R1 == R2);

View File

@ -210,4 +210,11 @@ concept UnitCompatibleWith =
Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(FromU))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
(!AssociatedUnit<U> || UnitOf<U, QS>)&&detail::UnitConvertibleTo<FromU, U{}>;
namespace detail {
template<typename T>
concept OffsetUnit = Unit<T> && requires { T::point_origin; };
}
} // namespace mp_units

View File

@ -114,8 +114,8 @@ template<ReferenceOf<dimensionless> auto R, typename Rep>
[[nodiscard]] constexpr quantity<R, Rep> exp(const quantity<R, Rep>& q)
{
using std::exp;
return value_cast<get_unit(R)>(quantity{static_cast<Rep>(exp(q.force_numerical_value_in(q.unit))),
detail::make_delta(detail::clone_reference_with<one>(R))});
return value_cast<get_unit(R)>(
quantity{static_cast<Rep>(exp(q.force_numerical_value_in(q.unit))), 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)),
detail::make_delta(common_reference(R * S, T))};
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)),
detail::make_delta(common_reference(R * S, T))};
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)), detail::make_delta(ref)};
return quantity{fmod(x.numerical_value_in(unit), y.numerical_value_in(unit)), 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)), detail::make_delta(ref)};
return quantity{remainder(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref};
}
@ -338,8 +338,8 @@ template<Unit auto To, auto R, typename Rep>
if constexpr (To == get_unit(R)) {
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::make_delta(detail::clone_reference_with<To>(R))});
return handle_signed_results(
quantity{static_cast<Rep>(floor(q.force_numerical_value_in(To))), detail::clone_reference_with<To>(R)});
}
} else {
if constexpr (To == get_unit(R)) {
@ -375,8 +375,8 @@ template<Unit auto To, auto R, typename Rep>
if constexpr (To == get_unit(R)) {
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::make_delta(detail::clone_reference_with<To>(R))});
return handle_signed_results(
quantity{static_cast<Rep>(ceil(q.force_numerical_value_in(To))), 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)), detail::make_delta(ref)};
return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref};
}
/**
@ -474,8 +474,7 @@ 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)),
detail::make_delta(ref)};
return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit), z.numerical_value_in(unit)), ref};
}
} // namespace mp_units

View File

@ -27,6 +27,7 @@
#include <mp-units/systems/si/prefixes.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <mp-units/framework/construction_helpers.h>
#include <mp-units/framework/quantity_point.h>
#include <mp-units/framework/unit.h>
#endif
@ -77,7 +78,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<273'150 * absolute<milli<kelvin>>> {} ice_point;
inline constexpr struct ice_point final : relative_point_origin<absolute<milli<kelvin>>(273'150)> {} 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<-32 * absolute<mag_ratio<5, 9> * si::degree_Celsius>> {} zeroth_degree_Fahrenheit;
inline constexpr struct zeroth_degree_Fahrenheit final : relative_point_origin<absolute<mag_ratio<5, 9> * si::degree_Celsius>(-32)> {} 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,12 +801,11 @@ 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 * 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(quantity_point{delta<deg_C>(20)}.quantity_from_zero() == delta<deg_C>(20));
static_assert(quantity_point{delta<deg_C>(20.)}.in(deg_F).quantity_from_zero() == delta<deg_F>(68));
static_assert(absolute<deg_C>(20).quantity_from_zero() == delta<deg_C>(20));
static_assert(absolute<deg_C>(20.).in(deg_F).quantity_from_zero() == delta<deg_F>(68));
static_assert((mean_sea_level + 42 * m).quantity_from_zero() == 42 * m);
static_assert((ground_level + 42 * m).quantity_from_zero() == 84 * m);
@ -892,12 +891,13 @@ 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(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)>,
static_assert(std::is_same_v<decltype(quantity_point{delta<deg_C>(20)})::rep, int>);
static_assert(
std::is_same_v<std::remove_const_t<decltype(quantity_point{delta<deg_C>(20)}.point_origin)>, struct si::ice_point>);
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{delta<deg_C>(20)}.absolute_point_origin)>,
struct si::absolute_zero>);
static_assert((20 * absolute<deg_C>).unit == si::degree_Celsius);
static_assert((20 * absolute<deg_C>).quantity_spec == kind_of<isq::thermodynamic_temperature>);
static_assert(quantity_point{delta<deg_C>(20)}.unit == si::degree_Celsius);
static_assert(quantity_point{delta<deg_C>(20)}.quantity_spec == kind_of<isq::thermodynamic_temperature>);
#if MP_UNITS_HOSTED
using namespace std::chrono_literals;

View File

@ -126,7 +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 * delta<deg_F>) ==
isq::thermodynamic_temperature(5 * delta<si::degree_Celsius>));
static_assert(delta<isq::thermodynamic_temperature[deg_F]>(9) ==
delta<isq::thermodynamic_temperature[si::degree_Celsius]>(5));
} // namespace