mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
feat: 💥 delta
and absolute
construction helpers
This commit is contained in:
@ -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" }
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
```
|
||||
|
@ -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.
|
||||
{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:
|
||||
{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{};
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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)));
|
||||
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
67
src/core/include/mp-units/framework/construction_helpers.h
Normal file
67
src/core/include/mp-units/framework/construction_helpers.h
Normal 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
|
@ -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>
|
||||
|
@ -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)};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user