mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-02 03:44:27 +02:00
Merge pull request #522 from mpusz/implicit_point_origin
feat: implementation of implicit point origins
This commit is contained in:
12
.github/workflows/ci-conan.yml
vendored
12
.github/workflows/ci-conan.yml
vendored
@@ -54,18 +54,6 @@ jobs:
|
||||
# compiler: { type: MSVC, version: 193, cc: "", cxx: "" },
|
||||
# conan-config: "",
|
||||
# }
|
||||
- {
|
||||
name: "GCC-11",
|
||||
os: ubuntu-22.04,
|
||||
compiler:
|
||||
{
|
||||
type: GCC,
|
||||
version: 11,
|
||||
cc: "gcc-11",
|
||||
cxx: "g++-11",
|
||||
},
|
||||
conan-config: "",
|
||||
}
|
||||
- {
|
||||
name: "GCC-12",
|
||||
os: ubuntu-22.04,
|
||||
|
11
.github/workflows/ci-test-package-cmake.yml
vendored
11
.github/workflows/ci-test-package-cmake.yml
vendored
@@ -53,17 +53,6 @@ jobs:
|
||||
# os: windows-2022,
|
||||
# compiler: { type: MSVC, version: 193, cc: "", cxx: "" },
|
||||
# }
|
||||
- {
|
||||
name: "GCC-11",
|
||||
os: ubuntu-22.04,
|
||||
compiler:
|
||||
{
|
||||
type: GCC,
|
||||
version: 11,
|
||||
cc: "gcc-11",
|
||||
cxx: "g++-11",
|
||||
},
|
||||
}
|
||||
- {
|
||||
name: "GCC-12",
|
||||
os: ubuntu-22.04,
|
||||
|
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -37,11 +37,11 @@ jobs:
|
||||
contents: read
|
||||
security-events: write
|
||||
env:
|
||||
CC: gcc-11
|
||||
CXX: g++-11
|
||||
CC: gcc-12
|
||||
CXX: g++-12
|
||||
BUILD_TYPE: Debug
|
||||
COMPILER_TYPE: GCC
|
||||
COMPILER_VERSION: 11
|
||||
COMPILER_VERSION: 12
|
||||
STDLIB: libstdc++11
|
||||
|
||||
strategy:
|
||||
|
@@ -73,7 +73,7 @@ class MPUnitsConan(ConanFile):
|
||||
@property
|
||||
def _minimum_compilers_version(self):
|
||||
return {
|
||||
"gcc": "11",
|
||||
"gcc": "12",
|
||||
"clang": "16",
|
||||
"apple-clang": "15"
|
||||
# , "msvc": "192"
|
||||
|
@@ -96,10 +96,10 @@ to form a quantity.
|
||||
|
||||
!!! note
|
||||
|
||||
The same applies to the `quantity_point` construction. To prevent similar issues during
|
||||
construction, it always needs to get both a `quantity` and
|
||||
a [`PointOrigin`](../users_guide/framework_basics/concepts.md#PointOrigin) that we use
|
||||
as a reference point.
|
||||
The same applies to the construction of `quantity_point` using an explicit point origin.
|
||||
To prevent similar safety issues during maintenance, the initialization always requires
|
||||
providing both a `quantity` and a [`PointOrigin`](../users_guide/framework_basics/concepts.md#PointOrigin)
|
||||
that we use as a reference point.
|
||||
|
||||
|
||||
## Why a dimensionless quantity is not just a fundamental arithmetic type?
|
||||
|
@@ -14,9 +14,7 @@
|
||||
|
||||
As of today, the library compiles fine on the following compilers (or newer):
|
||||
|
||||
- gcc-11
|
||||
- please note that we observed some ICEs on gcc-11
|
||||
- no problems with gcc-12.2+
|
||||
- gcc-12
|
||||
- clang-16
|
||||
- apple-clang-15
|
||||
|
||||
|
@@ -86,6 +86,8 @@ static_assert(140 * km / (2 * h) == 70 * km / h);
|
||||
## Quantity points
|
||||
|
||||
The quantity point specifies an absolute quantity with respect to an origin.
|
||||
If no origin is provided explicitly, an implicit one will be provided by the library.
|
||||
|
||||
Together with quantities, they model [The Affine Space](../users_guide/framework_basics/the_affine_space.md).
|
||||
|
||||
Quantity points should be used in all places where adding two values is meaningless
|
||||
@@ -106,10 +108,10 @@ int main()
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using namespace mp_units::usc::unit_symbols;
|
||||
|
||||
quantity_point temp = si::zeroth_degree_Celsius + 20. * deg_C;
|
||||
quantity_point temp{20. * deg_C};
|
||||
std::cout << "Temperature: "
|
||||
<< temp.quantity_from(si::zeroth_degree_Celsius) << " ("
|
||||
<< temp.in(deg_F).quantity_from(usc::zeroth_degree_Fahrenheit) << ")\n";
|
||||
<< temp.quantity_from_zero() << " ("
|
||||
<< temp.in(deg_F).quantity_from_zero() << ")\n";
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -27,9 +27,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units)
|
||||
|
||||
As of today, the library compiles fine on the following compilers (or newer):
|
||||
|
||||
- gcc-11
|
||||
- please note that we observed some ICEs on gcc-11
|
||||
- no problems with gcc-12.2+
|
||||
- gcc-12
|
||||
- clang-16
|
||||
- apple-clang-15
|
||||
|
||||
|
@@ -68,145 +68,245 @@ As we already know, a `quantity` type provides all operations required for a _ve
|
||||
the affine space.
|
||||
|
||||
|
||||
## _Point_ is modeled by `PointOrigin` and `quantity_point`
|
||||
## _Point_ is modeled by `quantity_point` and `PointOrigin`
|
||||
|
||||
In the **mp-units** library the _point_ abstraction is modelled by:
|
||||
|
||||
- [`PointOrigin` concept](concepts.md#PointOrigin) that specifies measurement origin,
|
||||
- `quantity_point` class template that specifies a _point_ relative to a specific predefined origin.
|
||||
|
||||
### Absolute _point_ origin
|
||||
|
||||
The **absolute point origin** specifies where the "zero" of our measurement's scale is. User can
|
||||
specify such an origin by deriving from the `absolute_point_origin` class template:
|
||||
|
||||
```cpp
|
||||
constexpr struct mean_sea_level : absolute_point_origin<mean_sea_level, isq::altitude> {} mean_sea_level;
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
The `absolute_point_origin` class template uses CRTP idiom to enforce the uniqueness of such a type.
|
||||
You should pass the type of a derived class as the first argument of the template instantiation.
|
||||
|
||||
*[CRTP]: Curiously Recurring Template Parameter
|
||||
|
||||
### `quantity_point`
|
||||
|
||||
The `quantity_point` class template specifies an absolute quantity with respect to an origin:
|
||||
The `quantity_point` class template specifies an absolute quantity measured from a predefined
|
||||
origin:
|
||||
|
||||
```cpp
|
||||
template<Reference auto R,
|
||||
PointOriginFor<get_quantity_spec(R)> auto PO,
|
||||
PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R),
|
||||
RepresentationOf<get_quantity_spec(R).character> Rep = double>
|
||||
class quantity_point;
|
||||
```
|
||||
|
||||
As we can see above, the `quantity_point` class template exposes one additional parameter compared
|
||||
to `quantity`. The `PO` parameter satisfies a [`PointOriginFor` concept](concepts.md#PointOriginFor)
|
||||
and specifies the origin of our measurement scale.
|
||||
and specifies the origin of our measurement scale. By default, it is initialized with a quantity's
|
||||
zeroth point using the following rules:
|
||||
|
||||
- if the measurement unit of a quantity specifies its point origin in its definition
|
||||
(e.g., degree Celsius), then this point is being used,
|
||||
- otherwise, an instantiation of `zeroth_point_origin<QuantitySpec>` is being used which
|
||||
provides a zeroth point for a specific quantity type.
|
||||
|
||||
!!! tip
|
||||
|
||||
`quantity_point` definition can be found in the `mp-units/quantity_point.h` header file.
|
||||
The `quantity_point` definition can be found in the `mp-units/quantity_point.h` header file.
|
||||
|
||||
As a _point_ can be represented with a _vector_ from the origin, a `quantity_point` class
|
||||
template can be created with the following operations:
|
||||
|
||||
### Implicit point origin
|
||||
|
||||
Let's assume that Alice goes for a trip driving a car. She likes taking notes about interesting
|
||||
places that she visits on the road. For every such item, she writes down:
|
||||
|
||||
- its name,
|
||||
- a readout from the car's odometer at the location,
|
||||
- a current timestamp.
|
||||
|
||||
We can implement this in the following way:
|
||||
|
||||
```cpp
|
||||
quantity_point qp1 = mean_sea_level + 42 * m;
|
||||
quantity_point qp2 = 42 * m + mean_sea_level;
|
||||
quantity_point qp3 = mean_sea_level - 42 * m;
|
||||
using std::chrono::system_clock;
|
||||
|
||||
struct trip_log_item {
|
||||
std::string name;
|
||||
quantity_point<isq::distance[km]> odometer;
|
||||
quantity_point<si::second> timestamp;
|
||||
};
|
||||
using trip_log = std::vector<trip_log_item>;
|
||||
```
|
||||
|
||||
```cpp
|
||||
trip_log log;
|
||||
|
||||
quantity_point timestamp_1{quantity{system_clock::now().time_since_epoch()}};
|
||||
log.emplace_back("home", quantity_point{1356 * km}, timestamp_1);
|
||||
|
||||
// some time passes
|
||||
|
||||
quantity_point timestamp_2{quantity{system_clock::now().time_since_epoch()}};
|
||||
log.emplace_back("castle", quantity_point{1401 * km}, timestamp_2);
|
||||
```
|
||||
|
||||
This is an excellent example of where points are helpful. There is no doubt about the correctness
|
||||
of their usage in this scenario:
|
||||
|
||||
- adding two odometer readouts or two timestamps have no physical sense, and that is why we will
|
||||
expect a compile-time error when we try to perform such operations accidentally,
|
||||
- subtracting two odometer readouts or timestamps is perfectly valid and results in a quantity
|
||||
storing the interval value between the two points.
|
||||
|
||||
Having such a database, we can print the trip log in the following way:
|
||||
|
||||
```cpp
|
||||
for (const auto& item : log) {
|
||||
std::cout << "POI: " << item.name << "\n";
|
||||
std::cout << "- Distance from home: " << item.odometer - log.front().odometer;
|
||||
std::cout << "- Trip duration from start: " << (item.timestamp - log.front().timestamp).in(non_si::minute);
|
||||
}
|
||||
```
|
||||
|
||||
Moreover, if Alice had reset the car's trip odometer before leaving home, we could have rewritten
|
||||
one of the previous lines like that:
|
||||
|
||||
```cpp
|
||||
std::cout << "Distance from home: " << item.odometer.quantity_from_zero();
|
||||
```
|
||||
|
||||
The above always returns a quantity measured from the "ultimate" zeroth point of a scale used for
|
||||
this specific quantity type.
|
||||
|
||||
!!! tip
|
||||
|
||||
Storing _points_ is the most efficient representation we can choose in this scenario:
|
||||
|
||||
- to store a value, we read it directly from the instrument, and no additional transformation
|
||||
is needed,
|
||||
- to print the absolute value (e.g., odometer), we have the value available right away,
|
||||
- to get any relative quantity (e.g., distance from the start, distance from the previous point,
|
||||
etc.), we have to perform a single subtraction operation.
|
||||
|
||||
If we stored _vectors_ in our database instead, we would have to pay at runtime for additional
|
||||
operations:
|
||||
|
||||
- to store a quantity, we would have to perform the subtraction right away to get the interval
|
||||
between the current value and some reference point,
|
||||
- to print the absolute value, we would have to add the quantity to the reference point that
|
||||
we need to store somewhere in the database as well,
|
||||
- to get a relative quantity, only the currently stored one is immediate; all other values
|
||||
will require at least one quantity addition operation.
|
||||
|
||||
Now, let's assume that Bob, a friend of Alice, also keeps a log of his trips but he, of
|
||||
course, measures distances from his own home with the odometer in his car. Everything is fine as
|
||||
long as we deal with one trip at a time, but if we start to work with both at once, we may
|
||||
accidentally subtract points from different trips. The library will not prevent
|
||||
us from doing so.
|
||||
|
||||
The points from Alice's and Bob's trips should be considered separate, and to enforce it at
|
||||
compilation time, we need to introduce explicit origins.
|
||||
|
||||
|
||||
### Absolute _point_ origin
|
||||
|
||||
The **absolute point origin** specifies the "zero" of our measurement's scale. User can
|
||||
specify such an origin by deriving from the `absolute_point_origin` class template:
|
||||
|
||||
```cpp
|
||||
enum class actor { alice, bob };
|
||||
|
||||
template<actor A>
|
||||
struct zeroth_odometer_t : absolute_point_origin<zeroth_odometer_t<A>, isq::distance> {};
|
||||
|
||||
template<actor A>
|
||||
inline constexpr zeroth_odometer_t<A> zeroth_odometer;
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
The `absolute_point_origin` class template uses the CRTP idiom to enforce the uniqueness of
|
||||
such a type. You should pass the type of a derived class as the first argument of the template
|
||||
instantiation.
|
||||
|
||||
*[CRTP]: Curiously Recurring Template Parameter
|
||||
|
||||
!!! note
|
||||
|
||||
Unfortunately, due to inconsistencies in C++ language rules:
|
||||
|
||||
- we can't define the above in one line of code,
|
||||
- provide the same identifier for a class and variable template.
|
||||
|
||||
Odometer is not the only one that can get an explicit point origin in our case. As timestamps are
|
||||
provided by the `std::chrono::system_clock`, their values are always relative to the epoch of this
|
||||
clock.
|
||||
|
||||
!!! note
|
||||
|
||||
The **mp-units** library provides means to specify
|
||||
[interoperability with other units libraries](../use_cases/interoperability_with_other_units_libraries.md).
|
||||
It also has built-in compatibility with `std::chrono` types, so users do not have to define
|
||||
interoperability traits for such types by themselves. Those are provided in the
|
||||
`mp-units/chrono.h` header file.
|
||||
|
||||
|
||||
Now, we can refactor our database to benefit from the explicit points:
|
||||
|
||||
```cpp
|
||||
template<actor A>
|
||||
struct trip_log_item {
|
||||
std::string point_name;
|
||||
quantity_point<si::kilo<si::metre>, zeroth_odometer<A>> odometer;
|
||||
quantity_point<si::second, chrono_point_origin<system_clock>> timestamp;
|
||||
};
|
||||
|
||||
template<actor A>
|
||||
using trip_log = std::vector<trip_log_item<A>>;
|
||||
```
|
||||
|
||||
We also need to update the initialization part in our code. In the case of implicit zeroth origins,
|
||||
we could construct `quantity_point` directly from the value of a `quantity`. This is no longer
|
||||
the case.
|
||||
As a _point_ can be represented with a _vector_ from the origin, to improve the safety of the code
|
||||
we write, a `quantity_point` class template must be created with one of the following operations:
|
||||
|
||||
```cpp
|
||||
quantity_point qp1 = zeroth_odometer<actor::alice> + 1356 * km;
|
||||
quantity_point qp2 = 1356 * km + zeroth_odometer<actor::alice>;
|
||||
quantity_point qp3 = zeroth_odometer<actor::alice> - 1356 * km;
|
||||
```
|
||||
|
||||
Although, the `qp3` above does not have a physical sense in this specific scenario.
|
||||
|
||||
!!! note
|
||||
|
||||
[It is not allowed to subtract a _point_ from a _vector_](#operations-in-the-affine-space)
|
||||
thus `42 * m - mean_sea_level` is an invalid operation.
|
||||
thus `1356 * km - zeroth_odometer<actor::alice>` is an invalid operation.
|
||||
|
||||
!!! info
|
||||
|
||||
A rationale for this longer construction syntax can be found in the
|
||||
[Why can't I create a quantity by passing a number to a constructor?](../../getting_started/faq.md#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor)
|
||||
chapter.
|
||||
|
||||
Similarly to [creation of a quantity](../../getting_started/quick_start.md#creating-a-quantity),
|
||||
if someone does not like the operator-based syntax to create a `quantity_point`, the same results
|
||||
can be achieved with two-parameter constructor:
|
||||
can be achieved with a two-parameter constructor:
|
||||
|
||||
```cpp
|
||||
quantity_point qp4{42 * m, mean_sea_level};
|
||||
quantity_point qp5{-42 * m, mean_sea_level};
|
||||
quantity_point qp4{1356 * km, zeroth_odometer<actor::alice>};
|
||||
```
|
||||
|
||||
The provided `quantity` representing an offset from the origin is stored inside the `quantity_point`
|
||||
class template and can be obtained with a `quantity_from(PointOrigin)` member function:
|
||||
Also, as now our timestamps have a proper point origin provided in a type, we can simplify the
|
||||
previous code by directly converting `std::chrono::time_point` to our `quantity_point` type.
|
||||
|
||||
With all the above, we can refactor our initialization part to the following:
|
||||
|
||||
```cpp
|
||||
constexpr quantity_point everest_base_camp_alt = mean_sea_level + isq::altitude(5364 * m);
|
||||
static_assert(everest_base_camp_alt.quantity_from(mean_sea_level) == 5364 * m);
|
||||
trip_log<actor::alice> alice_log;
|
||||
|
||||
alice_log.emplace_back("home", zeroth_odometer<actor::alice> + 1356 * km, system_clock::now());
|
||||
|
||||
// some time passes
|
||||
|
||||
alice_log.emplace_back("castle", zeroth_odometer<actor::alice> + 1401 * km, system_clock::now());
|
||||
```
|
||||
|
||||
|
||||
### Relative _point_ origin
|
||||
|
||||
We often do not have only one ultimate "zero" point when we measure things.
|
||||
|
||||
Continuing the Mount Everest trip example above, measuring all daily hikes from the `mean_sea_level`
|
||||
might not be efficient. Maybe we know that we are not good climbers, so all our climbs can be
|
||||
represented with an 8-bit integer type allowing us to save memory in our database of climbs?
|
||||
Why not use `everest_base_camp_alt` as our reference point?
|
||||
|
||||
For this purpose, we can define a `relative_point_origin` in the following way:
|
||||
|
||||
```cpp
|
||||
constexpr struct everest_base_camp : relative_point_origin<everest_base_camp_alt> {} everest_base_camp;
|
||||
```
|
||||
|
||||
The above can be used as an origin for subsequent _points_:
|
||||
|
||||
```cpp
|
||||
constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m);
|
||||
static_assert(first_climb_alt.quantity_from(everest_base_camp) == 42 * m);
|
||||
static_assert(first_climb_alt.quantity_from(mean_sea_level) == 5406 * m);
|
||||
```
|
||||
|
||||
As we can see above, the `quantity_from()` member function returns a relative distance from the
|
||||
provided point origin.
|
||||
|
||||
|
||||
### Converting between different representations of the same _point_
|
||||
|
||||
As we might represent the same _point_ with _vectors_ from various origins, the **mp-units** library
|
||||
provides facilities to convert the _point_ to the `quantity_point` class templates expressed in terms
|
||||
of different origins.
|
||||
|
||||
For this purpose, we can either use:
|
||||
|
||||
- a converting constructor:
|
||||
|
||||
```cpp
|
||||
constexpr quantity_point<isq::altitude[m], mean_sea_level, int> qp = first_climb_alt;
|
||||
static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m);
|
||||
```
|
||||
|
||||
- a dedicated conversion interface:
|
||||
|
||||
```cpp
|
||||
constexpr quantity_point qp = first_climb_alt.point_for(mean_sea_level);
|
||||
static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m);
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
It is only allowed to convert between various origins defined in terms of the same
|
||||
`absolute_point_origin`. Even if it is possible to express the same _point_ as a _vector_
|
||||
from another `absolute_point_origin`, the library will not provide such a conversion.
|
||||
A custom user-defined conversion function will be needed to add this functionality.
|
||||
|
||||
Said otherwise, in the **mp-units** library, there is no way to spell how two distinct
|
||||
`absolute_point_origin` types relate to each other.
|
||||
|
||||
|
||||
### _Point_ arithmetics
|
||||
|
||||
Let's assume we will attend the CppCon conference hosted in Aurora, CO, and we want to estimate
|
||||
the distance we will travel. We have to take a taxi to a local airport, fly to DEN airport with
|
||||
a stopover in FRA, and, in the end, get a cab to the Gaylord Rockies Resort & Convention Center:
|
||||
As another example, let's assume we will attend the CppCon conference hosted in Aurora, CO,
|
||||
and we want to estimate the distance we will travel. We have to take a taxi to a local airport,
|
||||
fly to DEN airport with a stopover in FRA, and, in the end, get a cab to the Gaylord Rockies
|
||||
Resort & Convention Center:
|
||||
|
||||
```cpp
|
||||
constexpr struct home : absolute_point_origin<home, isq::distance> {} home;
|
||||
@@ -255,70 +355,203 @@ Taxi distance: 31.2544 km
|
||||
!!! note
|
||||
|
||||
It is not allowed to subtract two point origins defined in terms of `absolute_point_origin`
|
||||
(e.g. `mean_sea_level - mean_sea_level`) as those do not contain information about the unit
|
||||
so we are not able to determine a resulting `quantity` type.
|
||||
(e.g., `home - home`) as those do not contain information about the unit, so we are not able
|
||||
to determine a resulting `quantity` type.
|
||||
|
||||
|
||||
### Relative _point_ origin
|
||||
|
||||
We often do not have only one ultimate "zero" point when we measure things.
|
||||
|
||||
For example, let's assume that we have the following absolute point origin:
|
||||
|
||||
```cpp
|
||||
constexpr struct mean_sea_level : absolute_point_origin<mean_sea_level, isq::altitude> {} mean_sea_level;
|
||||
```
|
||||
|
||||
If we want to model a trip to Mount Everest, measuring all daily hikes from the `mean_sea_level`
|
||||
might not be efficient. We may know that we are not good climbers, so all our climbs can be
|
||||
represented with an 8-bit integer type, allowing us to save memory in our database of climbs.
|
||||
|
||||
For this purpose, we can define a `relative_point_origin` in the following way:
|
||||
|
||||
```cpp
|
||||
constexpr struct everest_base_camp : relative_point_origin<mean_sea_level + 5364 * m> {} everest_base_camp;
|
||||
```
|
||||
|
||||
The above can be used as an origin for subsequent _points_:
|
||||
|
||||
```cpp
|
||||
constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m);
|
||||
static_assert(first_climb_alt.quantity_from(everest_base_camp) == 42 * m);
|
||||
static_assert(first_climb_alt.quantity_from(mean_sea_level) == 5406 * m);
|
||||
static_assert(first_climb_alt.quantity_from_zero() == 5406 * m);
|
||||
```
|
||||
|
||||
As we can see above, the `quantity_from()` member function returns a relative distance from the
|
||||
provided point origin while the `quantity_from_zero()` returns the distance from the absolute point
|
||||
origin.
|
||||
|
||||
|
||||
### Converting between different representations of the same _point_
|
||||
|
||||
As we might represent the same _point_ with _vectors_ from various origins, the **mp-units** library
|
||||
provides facilities to convert the _point_ to `quantity_point` class templates expressed in
|
||||
terms of origins relative to each other in the type system.
|
||||
|
||||
For this purpose, we can use:
|
||||
|
||||
- a converting constructor:
|
||||
|
||||
```cpp
|
||||
constexpr quantity_point<isq::altitude[m], mean_sea_level, int> qp = first_climb_alt;
|
||||
static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m);
|
||||
```
|
||||
|
||||
- a dedicated conversion interface:
|
||||
|
||||
```cpp
|
||||
constexpr quantity_point qp = first_climb_alt.point_for(mean_sea_level);
|
||||
static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m);
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
It is only allowed to convert between various origins defined in terms of the same
|
||||
`absolute_point_origin`. Even if it is theoretically possible to express the same _point_ as
|
||||
a _vector_ from another `absolute_point_origin`, the library will not allow such a conversion.
|
||||
A custom user-defined conversion function will be needed to add this functionality.
|
||||
|
||||
Said otherwise, in the **mp-units** library, there is no way to spell how two distinct
|
||||
`absolute_point_origin` types relate to each other.
|
||||
|
||||
|
||||
### Temperature support
|
||||
|
||||
Another important example of [relative point origins](#relative-point-origins) is support
|
||||
of temperature quantity points in units different than kelvin [`K`].
|
||||
|
||||
The [SI](../../appendix/glossary.md#si) system definition in the **mp-units** library provides
|
||||
two predefined point origins:
|
||||
Another important example of [relative point origins](#relative-point-origins) is the support
|
||||
of temperature quantity points. The **mp-units** library provides a few predefined point origins
|
||||
for this purpose:
|
||||
|
||||
```cpp
|
||||
namespace mp_units::si {
|
||||
namespace si {
|
||||
|
||||
inline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;
|
||||
inline constexpr struct ice_point : relative_point_origin<absolute_zero + 273.15 * kelvin> {} ice_point;
|
||||
inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;
|
||||
|
||||
inline constexpr struct ice_point : relative_point_origin<quantity_point{273.15 * kelvin}> {} ice_point;
|
||||
inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;
|
||||
|
||||
}
|
||||
|
||||
namespace usc {
|
||||
|
||||
inline constexpr struct zeroth_degree_Fahrenheit :
|
||||
relative_point_origin<si::zeroth_degree_Celsius - 32 * (mag<ratio{5, 9}> * si::degree_Celsius)> {} zeroth_degree_Fahrenheit;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
With the above, we can be explicit what is the origin of our temperature point. For example, if
|
||||
we want to implement the degree Celsius scale we can do it as follows:
|
||||
The above is a great example of how point origins can be stacked on top of each other:
|
||||
|
||||
```cpp
|
||||
using Celsius_point = quantity_point<isq::Celsius_temperature[deg_C], si::ice_point>;
|
||||
```
|
||||
- `usc::zeroth_degree_Fahrenheit` is defined relative to `si::zeroth_degree_Celsius`
|
||||
- `si::zeroth_degree_Celsius` is defined relative to `si::zeroth_kelvin`.
|
||||
|
||||
!!! note
|
||||
|
||||
Notice that while stacking point origins, we can use not only different representation types
|
||||
but also different units for an origin and a _point_. In the above example, the relative
|
||||
point origin is defined in terms of `si::kelvin`, while the quantity point uses
|
||||
`si::degree_Celsius`.
|
||||
but also different units for origins and a _point_. In the above example, the relative
|
||||
point origin for degree Celsius is defined in terms of `si::kelvin`, while the quantity point
|
||||
for it will use `si::degree_Celsius` as a unit.
|
||||
|
||||
To play a bit with temperatures we can implement a simple room's AC temperature controller in
|
||||
The temperature point origins defined above are provided explicitly in the respective units'
|
||||
definitions:
|
||||
|
||||
```cpp
|
||||
namespace si {
|
||||
|
||||
inline constexpr struct kelvin :
|
||||
named_unit<"K", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;
|
||||
inline constexpr struct degree_Celsius :
|
||||
named_unit<basic_symbol_text{"°C", "`C"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;
|
||||
|
||||
}
|
||||
|
||||
namespace usc {
|
||||
|
||||
inline constexpr struct degree_Fahrenheit :
|
||||
named_unit<basic_symbol_text{"°F", "`F"}, mag<ratio{5, 9}> * si::degree_Celsius,
|
||||
zeroth_degree_Fahrenheit> {} degree_Fahrenheit;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Now let's see how we can benefit from the above definitions. We have quite a few alternatives to
|
||||
choose from here. Depending on our needs or taste we can:
|
||||
|
||||
- be explicit about the unit and origin:
|
||||
|
||||
```cpp
|
||||
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q1 = si::zeroth_degree_Celsius + 20.5 * deg_C;
|
||||
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q2 = {20.5 * deg_C, si::zeroth_degree_Celsius};
|
||||
quantity_point<si::degree_Celsius, si::zeroth_degree_Celsius> q3{20.5 * deg_C};
|
||||
```
|
||||
|
||||
- specify a unit and use its zeroth point origin implicitly:
|
||||
|
||||
```cpp
|
||||
quantity_point<si::degree_Celsius> q4 = si::zeroth_degree_Celsius + 20.5 * deg_C;
|
||||
quantity_point<si::degree_Celsius> q5 = {20.5 * deg_C, si::zeroth_degree_Celsius};
|
||||
quantity_point<si::degree_Celsius> q6{20.5 * deg_C};
|
||||
```
|
||||
|
||||
- benefit from CTAD:
|
||||
|
||||
```cpp
|
||||
quantity_point q7 = si::zeroth_degree_Celsius + 20.5 * deg_C;
|
||||
quantity_point q8 = {20.5 * deg_C, si::zeroth_degree_Celsius};
|
||||
quantity_point q9{20.5 * deg_C};
|
||||
```
|
||||
|
||||
In all of the above cases, we end up with the `quantity_point` of the same type and value.
|
||||
|
||||
To play a bit more with temperatures, we can implement a simple room AC temperature controller in
|
||||
the following way:
|
||||
|
||||
```cpp
|
||||
constexpr struct room_reference_temp : relative_point_origin<si::ice_point + 21 * deg_C> {} room_reference_temp;
|
||||
constexpr struct room_reference_temp : relative_point_origin<quantity_point{21 * deg_C}> {} room_reference_temp;
|
||||
using room_temp = quantity_point<isq::Celsius_temperature[deg_C], room_reference_temp>;
|
||||
|
||||
constexpr auto step_delta = isq::Celsius_temperature(0.5 * deg_C);
|
||||
constexpr int number_of_steps = 6;
|
||||
|
||||
room_temp room_low = room_reference_temp - number_of_steps * step_delta;
|
||||
room_temp room_high = room_reference_temp + number_of_steps * step_delta;
|
||||
room_temp room_ref{};
|
||||
room_temp room_low = room_ref - number_of_steps * step_delta;
|
||||
room_temp room_high = room_ref + number_of_steps * step_delta;
|
||||
|
||||
std::println("| {:<14} | {:^18} | {:^18} | {:^18} |", "Temperature", "Room reference", "Ice point", "Absolute zero");
|
||||
std::println("Room reference temperature: {} ({}, {})\n",
|
||||
room_ref.quantity_from_zero(),
|
||||
room_ref.in(usc::degree_Fahrenheit).quantity_from_zero(),
|
||||
room_ref.in(si::kelvin).quantity_from_zero());
|
||||
|
||||
std::println("| {:<14} | {:^18} | {:^18} | {:^18} |",
|
||||
"Temperature", "Room reference", "Ice point", "Absolute zero");
|
||||
std::println("|{0:=^16}|{0:=^20}|{0:=^20}|{0:=^20}|", "");
|
||||
|
||||
auto print = [&](std::string_view label, auto v){
|
||||
std::println("| {:<14} | {:^18} | {:^18} | {:^18} |",
|
||||
label, v - room_reference_temp, v - si::ice_point, v - si::absolute_zero);
|
||||
auto print = [&](std::string_view label, auto v) {
|
||||
fmt::println("| {:<14} | {:^18} | {:^18} | {:^18} |", label,
|
||||
v - room_reference_temp, v - si::ice_point, v - si::absolute_zero);
|
||||
};
|
||||
|
||||
print("Lowest", room_low);
|
||||
print("Default", room_reference_temp);
|
||||
print("Default", room_ref);
|
||||
print("Highest", room_high);
|
||||
```
|
||||
|
||||
The above prints:
|
||||
|
||||
```text
|
||||
Room reference temperature: 21 °C (69.8 °F, 294.15 K)
|
||||
|
||||
| Temperature | Room reference | Ice point | Absolute zero |
|
||||
|================|====================|====================|====================|
|
||||
| Lowest | -3 °C | 18 °C | 291.15 °C |
|
||||
|
@@ -271,9 +271,13 @@ The **mp-units** library comes with built-in interoperability with those types.
|
||||
|
||||
quantity_point qp4 = my_origin + 1 * s;
|
||||
auto tp4 = to_chrono_time_point(qp4); // Compile-time Error (1)
|
||||
|
||||
quantity_point qp5{1 * s};
|
||||
auto tp5 = to_chrono_time_point(qp5); // Compile-time Error (2)
|
||||
```
|
||||
|
||||
1. `my_origin` is not defined in terms of `chrono_point_origin<Clock>`.
|
||||
2. `zeroth_point_origin` is not defined in terms of `chrono_point_origin<Clock>`.
|
||||
|
||||
Here is an example of how interoperability described in this chapter can be used in practice:
|
||||
|
||||
|
@@ -33,8 +33,6 @@ inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
|
||||
|
||||
QUANTITY_SPEC(currency, dim_currency);
|
||||
|
||||
constexpr struct zero : absolute_point_origin<zero, currency> {} zero;
|
||||
|
||||
inline constexpr struct euro : named_unit<"EUR", kind_of<currency>> {} euro;
|
||||
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
|
||||
inline constexpr struct great_british_pound : named_unit<"GBP", kind_of<currency>> {} great_british_pound;
|
||||
@@ -90,18 +88,18 @@ quantity<To, Rep> exchange_to(quantity<From, Rep> q)
|
||||
template<ReferenceOf<currency> auto To, ReferenceOf<currency> auto From, auto PO, typename Rep>
|
||||
quantity_point<To, PO, Rep> exchange_to(quantity_point<From, PO, Rep> q)
|
||||
{
|
||||
return quantity_point{zero + static_cast<Rep>(exchange_rate<q.unit, get_unit(To)>() *
|
||||
(q - q.absolute_point_origin).numerical_value_in(q.unit)) *
|
||||
To};
|
||||
return quantity_point{
|
||||
static_cast<Rep>(exchange_rate<q.unit, get_unit(To)>() * q.quantity_from_zero().numerical_value_in(q.unit)) * To};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace unit_symbols;
|
||||
|
||||
quantity_point price_usd = zero + 100 * USD;
|
||||
quantity_point price_usd{100 * USD};
|
||||
quantity_point price_euro = exchange_to<euro>(price_usd);
|
||||
|
||||
std::cout << price_usd.quantity_from(zero) << " -> " << price_euro.quantity_from(zero) << "\n";
|
||||
// std::cout << price_usd.quantity_from(zero) + price_euro.quantity_from(zero) << "\n"; // does not compile
|
||||
std::cout << price_usd.quantity_from_zero() << " -> " << price_euro.quantity_from_zero() << "\n";
|
||||
// std::cout << price_usd.quantity_from_zero() + price_euro.quantity_from_zero() << "\n"; // does
|
||||
// not compile
|
||||
}
|
||||
|
@@ -178,10 +178,10 @@ distance spherical_distance(position<T> from, position<T> to)
|
||||
|
||||
using si::sin, si::cos, si::asin, si::acos;
|
||||
|
||||
const quantity from_lat = from.lat.quantity_from(equator);
|
||||
const quantity from_lon = from.lon.quantity_from(prime_meridian);
|
||||
const quantity to_lat = to.lat.quantity_from(equator);
|
||||
const quantity to_lon = to.lon.quantity_from(prime_meridian);
|
||||
const quantity from_lat = from.lat.quantity_from_zero();
|
||||
const quantity from_lon = from.lon.quantity_from_zero();
|
||||
const quantity to_lat = to.lat.quantity_from_zero();
|
||||
const quantity to_lon = to.lon.quantity_from_zero();
|
||||
|
||||
// https://en.wikipedia.org/wiki/Great-circle_distance#Formulae
|
||||
if constexpr (sizeof(T) >= 8) {
|
||||
|
@@ -97,8 +97,8 @@ template<earth_gravity_model M>
|
||||
hae_altitude<M> to_hae(msl_altitude msl, position<long double> pos)
|
||||
{
|
||||
const auto geoid_undulation =
|
||||
isq::height(GeographicLibWhatsMyOffset(pos.lat.quantity_from(equator).numerical_value_in(si::degree),
|
||||
pos.lon.quantity_from(prime_meridian).numerical_value_in(si::degree)) *
|
||||
isq::height(GeographicLibWhatsMyOffset(pos.lat.quantity_from_zero().numerical_value_in(si::degree),
|
||||
pos.lon.quantity_from_zero().numerical_value_in(si::degree)) *
|
||||
si::metre);
|
||||
return height_above_ellipsoid<M> + (msl - mean_sea_level - geoid_undulation);
|
||||
}
|
||||
|
@@ -63,19 +63,52 @@ struct relative_point_origin {
|
||||
static constexpr PointOrigin auto absolute_point_origin = QP.absolute_point_origin;
|
||||
};
|
||||
|
||||
template<QuantitySpec auto QS>
|
||||
struct zeroth_point_origin_ : absolute_point_origin<zeroth_point_origin_<QS>, QS> {};
|
||||
|
||||
template<QuantitySpec auto QS>
|
||||
inline constexpr zeroth_point_origin_<QS> zeroth_point_origin;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<PointOrigin PO>
|
||||
inline constexpr bool is_specialization_of_zeroth_point_origin = false;
|
||||
|
||||
template<auto QS>
|
||||
inline constexpr bool is_specialization_of_zeroth_point_origin<zeroth_point_origin_<QS>> = true;
|
||||
|
||||
template<PointOrigin PO>
|
||||
[[nodiscard]] consteval bool is_zeroth_point_origin(PO)
|
||||
{
|
||||
return is_specialization_of_zeroth_point_origin<PO>;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<PointOrigin PO1, PointOrigin PO2>
|
||||
[[nodiscard]] consteval bool operator==(PO1 po1, PO2 po2)
|
||||
{
|
||||
if constexpr (detail::AbsolutePointOrigin<PO1> && detail::AbsolutePointOrigin<PO2>)
|
||||
return is_same_v<typename PO1::_type_, typename PO2::_type_>;
|
||||
return is_same_v<typename PO1::_type_, typename PO2::_type_> ||
|
||||
(detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) &&
|
||||
interconvertible(po1.quantity_spec, po2.quantity_spec));
|
||||
else if constexpr (detail::RelativePointOrigin<PO1> && detail::RelativePointOrigin<PO2>)
|
||||
return PO1::quantity_point == PO2::quantity_point;
|
||||
else if constexpr (detail::RelativePointOrigin<PO1>)
|
||||
return detail::same_absolute_point_origins(po1, po2) &&
|
||||
detail::is_eq_zero(PO1::quantity_point.quantity_from(PO1::quantity_point.absolute_point_origin));
|
||||
detail::is_eq_zero(PO1::quantity_point.quantity_from_zero());
|
||||
else if constexpr (detail::RelativePointOrigin<PO2>)
|
||||
return detail::same_absolute_point_origins(po1, po2) &&
|
||||
detail::is_eq_zero(PO2::quantity_point.quantity_from(PO2::quantity_point.absolute_point_origin));
|
||||
detail::is_eq_zero(PO2::quantity_point.quantity_from_zero());
|
||||
}
|
||||
|
||||
template<Reference R>
|
||||
[[nodiscard]] consteval PointOriginFor<get_quantity_spec(R{})> auto default_point_origin(R)
|
||||
{
|
||||
if constexpr (requires { get_unit(R{}).point_origin; })
|
||||
return get_unit(R{}).point_origin;
|
||||
else
|
||||
return zeroth_point_origin<get_quantity_spec(R{})>;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
@@ -100,7 +133,7 @@ template<PointOrigin PO>
|
||||
* @tparam PO a type that represents the origin point from which the quantity point is measured from
|
||||
* @tparam Rep a type to be used to represent values of a quantity point
|
||||
*/
|
||||
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO,
|
||||
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R),
|
||||
RepresentationOf<get_quantity_spec(R).character> Rep = double>
|
||||
class quantity_point {
|
||||
public:
|
||||
@@ -135,7 +168,14 @@ public:
|
||||
quantity_point(quantity_point&&) = default;
|
||||
|
||||
template<typename Q>
|
||||
requires std::same_as<std::remove_cvref_t<Q>, quantity_type>
|
||||
requires QuantityOf<std::remove_cvref_t<Q>, get_quantity_spec(R)> && std::constructible_from<quantity_type, Q> &&
|
||||
(point_origin == default_point_origin(R)) && (implicitly_convertible(Q::quantity_spec, quantity_spec))
|
||||
constexpr explicit quantity_point(Q&& q) : quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Q>
|
||||
requires QuantityOf<std::remove_cvref_t<Q>, get_quantity_spec(R)> && std::constructible_from<quantity_type, Q>
|
||||
constexpr quantity_point(Q&& q, std::remove_const_t<decltype(PO)>) :
|
||||
quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
|
||||
{
|
||||
@@ -219,6 +259,20 @@ public:
|
||||
return *this - PO2{};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Quantity auto quantity_from_zero() const
|
||||
{
|
||||
if constexpr (requires { unit.point_origin; }) {
|
||||
// original quantity point unit can be lost in the below operation
|
||||
const auto q = quantity_from(unit.point_origin);
|
||||
if constexpr (requires { q.in(unit); })
|
||||
// restore the unit if possible (non-truncating)
|
||||
return q.in(unit);
|
||||
else
|
||||
return q;
|
||||
} else
|
||||
return quantity_from(absolute_point_origin);
|
||||
}
|
||||
|
||||
// unit conversions
|
||||
template<UnitCompatibleWith<unit, quantity_spec> U>
|
||||
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
|
||||
@@ -322,6 +376,9 @@ public:
|
||||
};
|
||||
|
||||
// CTAD
|
||||
template<Quantity Q>
|
||||
quantity_point(Q q) -> quantity_point<Q::reference, default_point_origin(Q::reference), typename Q::rep>;
|
||||
|
||||
template<Quantity Q, PointOriginFor<Q::quantity_spec> PO>
|
||||
quantity_point(Q q, PO) -> quantity_point<Q::reference, PO{}, typename Q::rep>;
|
||||
|
||||
@@ -339,6 +396,9 @@ template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
|
||||
const quantity<R2, Rep2>& q)
|
||||
requires requires { qp.quantity_ref_from(PO1) + q; }
|
||||
{
|
||||
if constexpr (detail::is_zeroth_point_origin(PO1))
|
||||
return quantity_point{qp.quantity_ref_from(PO1) + q};
|
||||
else
|
||||
return quantity_point{qp.quantity_ref_from(PO1) + q, PO1};
|
||||
}
|
||||
|
||||
@@ -373,6 +433,9 @@ template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
|
||||
const quantity<R2, Rep2>& q)
|
||||
requires requires { qp.quantity_ref_from(PO1) - q; }
|
||||
{
|
||||
if constexpr (detail::is_zeroth_point_origin(PO1))
|
||||
return quantity_point{qp.quantity_ref_from(PO1) - q};
|
||||
else
|
||||
return quantity_point{qp.quantity_ref_from(PO1) - q, PO1};
|
||||
}
|
||||
|
||||
|
@@ -1401,6 +1401,12 @@ template<QuantitySpec From, QuantitySpec To>
|
||||
return detail::convertible_impl(from, to) >= detail::specs_convertible_result::cast;
|
||||
}
|
||||
|
||||
template<QuantitySpec QS1, QuantitySpec QS2>
|
||||
[[nodiscard]] consteval bool interconvertible(QS1 qs1, QS2 qs2)
|
||||
{
|
||||
return implicitly_convertible(qs1, qs2) && implicitly_convertible(qs2, qs1);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<QuantitySpec Q>
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <mp-units/bits/external/type_traits.h>
|
||||
#include <mp-units/bits/get_associated_quantity.h>
|
||||
#include <mp-units/bits/magnitude.h>
|
||||
#include <mp-units/bits/quantity_point_concepts.h>
|
||||
#include <mp-units/bits/quantity_spec_concepts.h>
|
||||
#include <mp-units/bits/ratio.h>
|
||||
#include <mp-units/bits/symbol_text.h>
|
||||
@@ -39,6 +40,18 @@
|
||||
|
||||
namespace mp_units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<Unit U, bool = requires { U::point_origin; }>
|
||||
struct propagate_point_origin {};
|
||||
|
||||
template<Unit U>
|
||||
struct propagate_point_origin<U, true> {
|
||||
static constexpr auto point_origin = U::point_origin;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Unit being a scaled version of another unit
|
||||
*
|
||||
@@ -49,7 +62,7 @@ namespace mp_units {
|
||||
* instantiate this type automatically based on the unit arithmetic equation provided by the user.
|
||||
*/
|
||||
template<Magnitude auto M, Unit U>
|
||||
struct scaled_unit {
|
||||
struct scaled_unit : detail::propagate_point_origin<U> {
|
||||
static constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M;
|
||||
static constexpr U reference_unit{};
|
||||
};
|
||||
@@ -116,6 +129,14 @@ struct named_unit<Symbol, QS> {
|
||||
static constexpr auto quantity_spec = QS;
|
||||
};
|
||||
|
||||
template<basic_symbol_text Symbol, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
|
||||
requires(!Symbol.empty()) && detail::BaseDimension<std::remove_const_t<decltype(QS.dimension)>>
|
||||
struct named_unit<Symbol, QS, PO> {
|
||||
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
|
||||
static constexpr auto quantity_spec = QS;
|
||||
static constexpr auto point_origin = PO;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Specialization for a unit that can be reused by several base quantities
|
||||
*
|
||||
@@ -146,6 +167,13 @@ struct named_unit<Symbol, U> : std::remove_const_t<decltype(U)> {
|
||||
static constexpr auto symbol = Symbol; ///< Unique unit identifier
|
||||
};
|
||||
|
||||
template<basic_symbol_text Symbol, Unit auto U, PointOrigin auto PO>
|
||||
requires(!Symbol.empty())
|
||||
struct named_unit<Symbol, U, PO> : std::remove_const_t<decltype(U)> {
|
||||
static constexpr auto symbol = Symbol; ///< Unique unit identifier
|
||||
static constexpr auto point_origin = PO;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Specialization for a unit with special name valid only for a specific quantity
|
||||
*
|
||||
@@ -162,6 +190,14 @@ struct named_unit<Symbol, U, QS> : std::remove_const_t<decltype(U)> {
|
||||
static constexpr auto quantity_spec = QS;
|
||||
};
|
||||
|
||||
template<basic_symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
|
||||
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
|
||||
struct named_unit<Symbol, U, QS, PO> : std::remove_const_t<decltype(U)> {
|
||||
static constexpr auto symbol = Symbol; ///< Unique unit identifier
|
||||
static constexpr auto quantity_spec = QS;
|
||||
static constexpr auto point_origin = PO;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A prefixed unit
|
||||
*
|
||||
@@ -286,14 +322,14 @@ canonical_unit(M, U) -> canonical_unit<M, U>;
|
||||
|
||||
#endif
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&);
|
||||
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q, auto... Args>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q, Args...>&);
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&);
|
||||
template<Unit T, basic_symbol_text Symbol, auto... Args>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&);
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol, Unit auto U>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&);
|
||||
template<Unit T, basic_symbol_text Symbol, Unit auto U, auto... Args>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&);
|
||||
|
||||
template<typename T, typename F, int Num, int... Den>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&);
|
||||
@@ -308,20 +344,20 @@ template<Unit T, auto M, typename U>
|
||||
return canonical_unit{M * base.mag, base.reference_unit};
|
||||
}
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&)
|
||||
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q, auto... Args>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q, Args...>&)
|
||||
{
|
||||
return canonical_unit{mag<1>, t};
|
||||
}
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&)
|
||||
template<Unit T, basic_symbol_text Symbol, auto... Args>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&)
|
||||
{
|
||||
return canonical_unit{mag<1>, t};
|
||||
}
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol, Unit auto U>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&)
|
||||
template<Unit T, basic_symbol_text Symbol, Unit auto U, auto... Args>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&)
|
||||
{
|
||||
return get_canonical_unit_impl(U, U);
|
||||
}
|
||||
|
@@ -1,38 +0,0 @@
|
||||
// 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/quantity_point.h>
|
||||
#include <mp-units/systems/si/units.h>
|
||||
|
||||
namespace mp_units::si {
|
||||
|
||||
// clang-format off
|
||||
inline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;
|
||||
inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;
|
||||
|
||||
inline constexpr struct ice_point : relative_point_origin<absolute_zero + 273.15 * kelvin> {} ice_point;
|
||||
inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;
|
||||
// clang-format on
|
||||
|
||||
} // namespace mp_units::si
|
@@ -23,7 +23,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <mp-units/systems/si/constants.h>
|
||||
#include <mp-units/systems/si/point_origins.h>
|
||||
#include <mp-units/systems/si/prefixes.h>
|
||||
#include <mp-units/systems/si/unit_symbols.h>
|
||||
#include <mp-units/systems/si/units.h>
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mp-units/quantity_point.h>
|
||||
#include <mp-units/systems/isq/atomic_and_nuclear_physics.h>
|
||||
#include <mp-units/systems/isq/base_quantities.h>
|
||||
#include <mp-units/systems/isq/space_and_time.h>
|
||||
@@ -39,7 +40,11 @@ inline constexpr struct metre : named_unit<"m", kind_of<isq::length>> {} metre;
|
||||
inline constexpr struct gram : named_unit<"g", kind_of<isq::mass>> {} gram;
|
||||
inline constexpr struct kilogram : decltype(kilo<gram>) {} kilogram;
|
||||
inline constexpr struct ampere : named_unit<"A", kind_of<isq::electric_current>> {} ampere;
|
||||
inline constexpr struct kelvin : named_unit<"K", kind_of<isq::thermodynamic_temperature>> {} kelvin;
|
||||
|
||||
inline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;
|
||||
inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;
|
||||
inline constexpr struct kelvin : named_unit<"K", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;
|
||||
|
||||
inline constexpr struct mole : named_unit<"mol", kind_of<isq::amount_of_substance>> {} mole;
|
||||
inline constexpr struct candela : named_unit<"cd", kind_of<isq::luminous_intensity>> {} candela;
|
||||
|
||||
@@ -68,7 +73,11 @@ inline constexpr struct siemens : named_unit<"S", one / ohm> {} siemens;
|
||||
inline constexpr struct weber : named_unit<"Wb", volt * second> {} weber;
|
||||
inline constexpr struct tesla : named_unit<"T", weber / square(metre)> {} tesla;
|
||||
inline constexpr struct henry : named_unit<"H", weber / ampere> {} henry;
|
||||
inline constexpr struct degree_Celsius : named_unit<basic_symbol_text{"°C", "`C"}, kelvin> {} degree_Celsius;
|
||||
|
||||
inline constexpr struct ice_point : relative_point_origin<quantity_point{273.15 * kelvin}> {} ice_point;
|
||||
inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;
|
||||
inline constexpr struct degree_Celsius : named_unit<basic_symbol_text{"°C", "`C"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;
|
||||
|
||||
inline constexpr struct lumen : named_unit<"lm", candela * steradian> {} lumen;
|
||||
inline constexpr struct lux : named_unit<"lx", lumen / square(metre)> {} lux;
|
||||
inline constexpr struct becquerel : named_unit<"Bq", one / second, kind_of<isq::activity>> {} becquerel;
|
||||
|
@@ -110,9 +110,9 @@ inline constexpr struct troy_pound : named_unit<"lb t", mag<12> * troy_once> {}
|
||||
inline constexpr struct inch_of_mercury : 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 degree_Fahrenheit : named_unit<basic_symbol_text{"°F", "`F"}, mag<ratio{5, 9}> * si::degree_Celsius> {} degree_Fahrenheit;
|
||||
inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin<si::zeroth_degree_Celsius - 32 * (mag<ratio{5, 9}> * si::degree_Celsius)> {} zeroth_degree_Fahrenheit;
|
||||
inline constexpr struct degree_Fahrenheit : named_unit<basic_symbol_text{"°F", "`F"}, mag<ratio{5, 9}> * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_Fahrenheit;
|
||||
|
||||
inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin<si::zeroth_degree_Celsius - 32 * degree_Fahrenheit> {} zeroth_degree_Fahrenheit;
|
||||
// clang-format on
|
||||
|
||||
namespace unit_symbols {
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <mp-units/quantity_point.h>
|
||||
#include <mp-units/systems/isq/isq.h>
|
||||
#include <mp-units/systems/si/si.h>
|
||||
#include <mp-units/systems/usc/usc.h>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -33,9 +34,13 @@ namespace {
|
||||
|
||||
using namespace mp_units;
|
||||
using namespace mp_units::si::unit_symbols;
|
||||
using namespace mp_units::usc::unit_symbols;
|
||||
using namespace std::chrono_literals;
|
||||
using sys_seconds = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
|
||||
|
||||
inline constexpr struct zeroth_length : absolute_point_origin<zeroth_length, isq::length> {
|
||||
} zeroth_length;
|
||||
|
||||
inline constexpr struct mean_sea_level : absolute_point_origin<mean_sea_level, isq::height> {
|
||||
} mean_sea_level;
|
||||
|
||||
@@ -88,6 +93,7 @@ static_assert(my_ground_level == ground_level);
|
||||
static_assert(same_ground_level1 == ground_level);
|
||||
static_assert(same_ground_level2 == my_ground_level);
|
||||
|
||||
static_assert(mean_sea_level != zeroth_length);
|
||||
static_assert(mean_sea_level != other_absolute_level);
|
||||
static_assert(my_mean_sea_level != other_absolute_level);
|
||||
static_assert(ground_level != other_ground_level);
|
||||
@@ -106,6 +112,31 @@ static_assert(relative_po<absolute_po<isq::length> + isq::height(42 * m)>.quanti
|
||||
static_assert(relative_po<absolute_po<kind_of<isq::length>> + isq::height(42 * m)>.quantity_spec == isq::height);
|
||||
static_assert(relative_po<absolute_po<isq::height> + 42 * m>.quantity_spec == isq::height);
|
||||
|
||||
inline constexpr struct my_kelvin : named_unit<"my_K", mag<10> * si::kelvin> {
|
||||
} my_kelvin;
|
||||
|
||||
static_assert(default_point_origin(si::kelvin) == si::absolute_zero);
|
||||
static_assert(default_point_origin(si::milli<si::kelvin>) == si::absolute_zero);
|
||||
static_assert(default_point_origin(mag<10> * si::kelvin) == si::absolute_zero);
|
||||
static_assert(default_point_origin(my_kelvin) == si::absolute_zero);
|
||||
|
||||
static_assert(default_point_origin(si::degree_Celsius) == si::ice_point);
|
||||
static_assert(default_point_origin(mag<10> * si::degree_Celsius) == si::ice_point);
|
||||
|
||||
static_assert(default_point_origin(si::metre) == zeroth_point_origin<kind_of<isq::length>>);
|
||||
static_assert(default_point_origin(si::kelvin / si::second) ==
|
||||
zeroth_point_origin<kind_of<isq::thermodynamic_temperature / isq::time>>);
|
||||
static_assert(default_point_origin(si::degree_Celsius / si::second) ==
|
||||
zeroth_point_origin<kind_of<isq::thermodynamic_temperature / isq::time>>);
|
||||
|
||||
static_assert(zeroth_point_origin<isq::length / isq::time> == zeroth_point_origin<isq::speed>);
|
||||
static_assert(zeroth_point_origin<inverse(isq::period_duration)> == zeroth_point_origin<isq::frequency>);
|
||||
static_assert(zeroth_point_origin<kind_of<isq::length>> == zeroth_point_origin<isq::height>);
|
||||
static_assert(zeroth_point_origin<kind_of<inverse(isq::time)>> == zeroth_point_origin<isq::frequency>);
|
||||
|
||||
static_assert(zeroth_point_origin<isq::length> != zeroth_point_origin<isq::height>);
|
||||
static_assert(zeroth_point_origin<isq::width> != zeroth_point_origin<isq::height>);
|
||||
static_assert(zeroth_point_origin<inverse(isq::time)> != zeroth_point_origin<isq::frequency>);
|
||||
|
||||
/////////////////////
|
||||
// class invariants
|
||||
@@ -126,6 +157,8 @@ concept invalid_types = requires {
|
||||
requires !requires { typename QP<isq::width[m], ground_level, int>; };
|
||||
requires !requires { typename QP<isq::length[m], mean_sea_level, int>; };
|
||||
requires !requires { typename QP<isq::length[m], ground_level, int>; };
|
||||
requires !requires { typename QP<isq::length[m], zeroth_point_origin<isq::height>, int>; };
|
||||
requires !requires { typename QP<isq::width[m], zeroth_point_origin<isq::height>, int>; };
|
||||
// quantity used as Rep
|
||||
requires !requires { typename QP<si::metre, mean_sea_level, quantity<si::metre, int>>; };
|
||||
// quantity point used as Rep
|
||||
@@ -151,6 +184,9 @@ concept valid_types = requires {
|
||||
typename QP<si::metre, ground_level, int>;
|
||||
typename QP<isq::height[m], ground_level, int>;
|
||||
typename QP<special_height[m], ground_level, int>;
|
||||
typename QP<isq::height[m], zeroth_point_origin<isq::length>, int>;
|
||||
typename QP<isq::height[m], zeroth_point_origin<kind_of<isq::length>>, int>;
|
||||
typename QP<si::metre, zeroth_point_origin<isq::height>, int>;
|
||||
};
|
||||
static_assert(valid_types<quantity_point>);
|
||||
|
||||
@@ -185,6 +221,20 @@ static_assert(std::three_way_comparable<quantity_point<si::metre, mean_sea_level
|
||||
// member values
|
||||
//////////////////
|
||||
|
||||
static_assert(quantity_point<si::metre>::reference == si::metre);
|
||||
static_assert(quantity_point<si::metre>::quantity_spec == kind_of<isq::length>);
|
||||
static_assert(quantity_point<si::metre>::dimension == isq::dim_length);
|
||||
static_assert(quantity_point<si::metre>::unit == si::metre);
|
||||
static_assert(is_of_type<quantity_point<si::metre>::point_origin, zeroth_point_origin_<kind_of<isq::length>>>);
|
||||
static_assert(is_of_type<quantity_point<si::metre>::absolute_point_origin, zeroth_point_origin_<kind_of<isq::length>>>);
|
||||
|
||||
static_assert(quantity_point<isq::height[m]>::reference == isq::height[m]);
|
||||
static_assert(quantity_point<isq::height[m]>::quantity_spec == isq::height);
|
||||
static_assert(quantity_point<isq::height[m]>::dimension == isq::dim_length);
|
||||
static_assert(quantity_point<isq::height[m]>::unit == si::metre);
|
||||
static_assert(is_of_type<quantity_point<isq::height[m]>::point_origin, zeroth_point_origin_<isq::height>>);
|
||||
static_assert(is_of_type<quantity_point<isq::height[m]>::absolute_point_origin, zeroth_point_origin_<isq::height>>);
|
||||
|
||||
static_assert(quantity_point<si::metre, mean_sea_level>::reference == si::metre);
|
||||
static_assert(quantity_point<si::metre, mean_sea_level>::quantity_spec == kind_of<isq::length>);
|
||||
static_assert(quantity_point<si::metre, mean_sea_level>::dimension == isq::dim_length);
|
||||
@@ -253,7 +303,7 @@ static_assert(quantity_point<si::degree_Celsius, si::ice_point>::dimension == is
|
||||
static_assert(quantity_point<si::degree_Celsius, si::ice_point>::unit == si::degree_Celsius);
|
||||
static_assert(is_of_type<quantity_point<si::degree_Celsius, si::ice_point>::point_origin, struct si::ice_point>);
|
||||
static_assert(
|
||||
is_of_type<quantity_point<si::degree_Celsius, si::ice_point>::absolute_point_origin, struct si::absolute_zero>);
|
||||
is_of_type<quantity_point<si::degree_Celsius, si::ice_point>::absolute_point_origin, struct si::zeroth_kelvin>);
|
||||
|
||||
static_assert(quantity_point<isq::Celsius_temperature[si::degree_Celsius], si::ice_point>::reference ==
|
||||
isq::Celsius_temperature[si::degree_Celsius]);
|
||||
@@ -266,7 +316,7 @@ static_assert(is_of_type<quantity_point<isq::Celsius_temperature[si::degree_Cels
|
||||
struct si::ice_point>);
|
||||
static_assert(
|
||||
is_of_type<quantity_point<isq::Celsius_temperature[si::degree_Celsius], si::ice_point>::absolute_point_origin,
|
||||
struct si::absolute_zero>);
|
||||
struct si::zeroth_kelvin>);
|
||||
|
||||
|
||||
//////////////////
|
||||
@@ -332,6 +382,80 @@ static_assert(!std::convertible_to<int, quantity_point<dimensionless[one], zero,
|
||||
// construction from a quantity
|
||||
/////////////////////////////////
|
||||
|
||||
// -----------------------------
|
||||
// implicit zeroth point origins
|
||||
// -----------------------------
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<si::metre>, quantity<si::metre>>);
|
||||
static_assert(!std::convertible_to<quantity<si::metre>, quantity_point<si::metre>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::height[m]>, quantity<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::height[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::height[m]>, quantity<si::metre>>);
|
||||
static_assert(!std::convertible_to<quantity<si::metre>, quantity_point<isq::height[m]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<si::metre>, quantity<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::height[m]>, quantity_point<si::metre>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<m / s>, quantity<isq::speed[m / s]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::speed[m / s]>, quantity_point<m / s>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::speed[m / s]>, quantity<m / s>>);
|
||||
static_assert(!std::convertible_to<quantity<m / s>, quantity_point<isq::speed[m / s]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::length[m] / isq::time[s]>, quantity<isq::speed[m / s]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::speed[m / s]>, quantity_point<isq::length[m] / isq::time[s]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::speed[m / s]>, quantity<isq::length[m] / isq::time[s]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::length[m] / isq::time[s]>, quantity_point<isq::speed[m / s]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::speed[m / s]>, quantity<m / s>>);
|
||||
static_assert(!std::convertible_to<quantity<m / s>, quantity_point<isq::speed[m / s]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<dimensionless[one]>, quantity<dimensionless[one]>>);
|
||||
static_assert(!std::convertible_to<quantity<dimensionless[one]>, quantity_point<dimensionless[one]>>);
|
||||
|
||||
// different dimensions
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity<si::second>>);
|
||||
static_assert(!std::convertible_to<quantity<si::second>, quantity_point<isq::height[m]>>);
|
||||
|
||||
// convertible but different quantity_specs
|
||||
static_assert(std::constructible_from<quantity_point<isq::length[m]>, quantity<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::height[m]>, quantity_point<isq::length[m]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::height[m]>, quantity<special_height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<special_height[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
// quantity_specs with common_quantity_spec
|
||||
static_assert(!std::constructible_from<quantity_point<isq::width[m]>, quantity<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::height[m]>, quantity_point<isq::width[m]>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity<isq::width[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::width[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
// non-convertible quantity_specs
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity<isq::length[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::length[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<special_height[m]>, quantity<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<special_height[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
// quantity-like
|
||||
static_assert(!std::constructible_from<quantity_point<si::second>, std::chrono::seconds>);
|
||||
static_assert(!std::convertible_to<std::chrono::seconds, quantity_point<si::second>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<isq::time[s]>, std::chrono::seconds>);
|
||||
static_assert(!std::convertible_to<std::chrono::seconds, quantity_point<isq::time[s]>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<isq::period_duration[s]>, std::chrono::seconds>);
|
||||
static_assert(!std::convertible_to<std::chrono::seconds, quantity_point<isq::period_duration[s]>>);
|
||||
|
||||
|
||||
// ----------------------
|
||||
// explicit point origins
|
||||
// ----------------------
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<si::metre, mean_sea_level>, quantity<si::metre>>);
|
||||
static_assert(!std::convertible_to<quantity<si::metre>, quantity_point<si::metre, mean_sea_level>>);
|
||||
|
||||
@@ -350,6 +474,13 @@ static_assert(!std::convertible_to<quantity<special_height[m]>, quantity_point<i
|
||||
static_assert(!std::constructible_from<quantity_point<dimensionless[one], zero>, quantity<dimensionless[one]>>);
|
||||
static_assert(!std::convertible_to<quantity<dimensionless[one]>, quantity_point<dimensionless[one], zero>>);
|
||||
|
||||
// quantity_specs with common_quantity_spec
|
||||
static_assert(!std::constructible_from<quantity_point<isq::width[m], zeroth_length>, quantity<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::height[m]>, quantity_point<isq::width[m], zeroth_length>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m], zeroth_length>, quantity<isq::width[m]>>);
|
||||
static_assert(!std::convertible_to<quantity<isq::width[m]>, quantity_point<isq::height[m], zeroth_length>>);
|
||||
|
||||
// different dimensions
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m], mean_sea_level>, quantity<si::second>>);
|
||||
static_assert(!std::convertible_to<quantity<si::second>, quantity_point<isq::height[m], mean_sea_level>>);
|
||||
@@ -388,7 +519,60 @@ static_assert(
|
||||
// construction from a quantity point
|
||||
///////////////////////////////////////
|
||||
|
||||
// same origins
|
||||
// implicit origin
|
||||
static_assert(std::constructible_from<quantity_point<si::metre>, quantity_point<si::metre>>);
|
||||
static_assert(std::convertible_to<quantity_point<si::metre>, quantity_point<si::metre>>);
|
||||
static_assert(std::constructible_from<quantity_point<isq::height[m]>, quantity_point<isq::height[m]>>);
|
||||
static_assert(std::convertible_to<quantity_point<isq::height[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<si::metre>, quantity_point<isq::height[m]>>);
|
||||
static_assert(std::convertible_to<quantity_point<isq::height[m]>, quantity_point<si::metre>>);
|
||||
static_assert(std::constructible_from<quantity_point<isq::height[m]>, quantity_point<si::metre>>);
|
||||
static_assert(std::convertible_to<quantity_point<si::metre>, quantity_point<isq::height[m]>>);
|
||||
|
||||
static_assert(
|
||||
std::constructible_from<quantity_point<isq::length[m] / isq::time[s]>, quantity_point<isq::speed[m / s]>>);
|
||||
static_assert(std::convertible_to<quantity_point<isq::speed[m / s]>, quantity_point<isq::length[m] / isq::time[s]>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<m / s>, quantity_point<isq::speed[m / s]>>);
|
||||
static_assert(std::convertible_to<quantity_point<isq::speed[m / s]>, quantity_point<m / s>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<m / s>, quantity_point<isq::length[m] / isq::time[s]>>);
|
||||
static_assert(std::convertible_to<quantity_point<isq::length[m] / isq::time[s]>, quantity_point<m / s>>);
|
||||
|
||||
// convertible but different quantity_specs
|
||||
static_assert(!std::constructible_from<quantity_point<isq::length[m]>, quantity_point<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity_point<isq::height[m]>, quantity_point<isq::length[m]>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity_point<special_height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity_point<special_height[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
// quantity_specs with common_quantity_spec
|
||||
static_assert(!std::constructible_from<quantity_point<isq::width[m]>, quantity_point<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity_point<isq::height[m]>, quantity_point<isq::width[m]>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity_point<isq::width[m]>>);
|
||||
static_assert(!std::convertible_to<quantity_point<isq::width[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
// non-convertible quantity_specs
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity_point<isq::length[m]>>);
|
||||
static_assert(!std::convertible_to<quantity_point<isq::length[m]>, quantity_point<isq::height[m]>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<special_height[m]>, quantity_point<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity_point<isq::height[m]>, quantity_point<special_height[m]>>);
|
||||
|
||||
// mixed origins
|
||||
static_assert(!std::constructible_from<quantity_point<si::metre, mean_sea_level>, quantity_point<si::metre>>);
|
||||
static_assert(!std::convertible_to<quantity_point<si::metre>, quantity_point<si::metre, mean_sea_level>>);
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m], mean_sea_level>, quantity_point<isq::height[m]>>);
|
||||
static_assert(!std::convertible_to<quantity_point<isq::height[m]>, quantity_point<isq::height[m], mean_sea_level>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<si::metre>, quantity_point<si::metre, mean_sea_level>>);
|
||||
static_assert(!std::convertible_to<quantity_point<si::metre, mean_sea_level>, quantity_point<si::metre>>);
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m]>, quantity_point<isq::height[m], mean_sea_level>>);
|
||||
static_assert(!std::convertible_to<quantity_point<isq::height[m], mean_sea_level>, quantity_point<isq::height[m]>>);
|
||||
|
||||
// same explicit origins
|
||||
static_assert(
|
||||
std::constructible_from<quantity_point<si::metre, mean_sea_level>, quantity_point<si::metre, mean_sea_level>>);
|
||||
static_assert(
|
||||
@@ -485,6 +669,22 @@ static_assert(
|
||||
static_assert(
|
||||
std::convertible_to<quantity_point<dimensionless[one], zero>, quantity_point<dimensionless[percent], zero>>);
|
||||
|
||||
static_assert(std::constructible_from<quantity_point<isq::length[m], zeroth_length>,
|
||||
quantity_point<isq::height[m], zeroth_length>>);
|
||||
static_assert(
|
||||
std::convertible_to<quantity_point<isq::height[m], zeroth_length>, quantity_point<isq::length[m], zeroth_length>>);
|
||||
|
||||
// quantity_specs with common_quantity_spec
|
||||
static_assert(!std::constructible_from<quantity_point<isq::width[m], zeroth_length>,
|
||||
quantity_point<isq::height[m], zeroth_length>>);
|
||||
static_assert(
|
||||
!std::convertible_to<quantity_point<isq::height[m], zeroth_length>, quantity_point<isq::width[m], zeroth_length>>);
|
||||
|
||||
static_assert(!std::constructible_from<quantity_point<isq::height[m], zeroth_length>,
|
||||
quantity_point<isq::width[m], zeroth_length>>);
|
||||
static_assert(
|
||||
!std::convertible_to<quantity_point<isq::width[m], zeroth_length>, quantity_point<isq::height[m], zeroth_length>>);
|
||||
|
||||
// different dimensions
|
||||
static_assert(
|
||||
!std::constructible_from<quantity_point<isq::height[m], mean_sea_level>, quantity_point<si::kelvin, si::ice_point>>);
|
||||
@@ -589,6 +789,15 @@ static_assert(
|
||||
// obtaining a relative quantity
|
||||
//////////////////////////////////
|
||||
|
||||
static_assert(quantity_point{42 * m}.quantity_from_zero() == 42 * m);
|
||||
static_assert(quantity_point{isq::height(42 * m)}.quantity_from_zero() == 42 * m);
|
||||
static_assert(quantity_point{20 * deg_C}.quantity_from_zero() == 20 * deg_C);
|
||||
static_assert(quantity_point{20. * deg_C}.in(deg_F).quantity_from_zero() == 68 * deg_F);
|
||||
|
||||
static_assert((mean_sea_level + 42 * m).quantity_from_zero() == 42 * m);
|
||||
static_assert((ground_level + 42 * m).quantity_from_zero() == 84 * m);
|
||||
static_assert((tower_peak + 42 * m).quantity_from_zero() == 126 * m);
|
||||
|
||||
static_assert((mean_sea_level + 42 * m).quantity_from(mean_sea_level) == 42 * m);
|
||||
static_assert((mean_sea_level + isq::height(42 * m)).quantity_from(mean_sea_level) == 42 * m);
|
||||
|
||||
@@ -653,17 +862,43 @@ static_assert(invalid_unit_conversion<quantity_point>);
|
||||
// CTAD
|
||||
/////////
|
||||
|
||||
static_assert(std::is_same_v<decltype(quantity_point{123 * m})::rep, int>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{123 * m}.point_origin)>,
|
||||
zeroth_point_origin_<kind_of<isq::length>>>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{123 * m}.absolute_point_origin)>,
|
||||
zeroth_point_origin_<kind_of<isq::length>>>);
|
||||
static_assert(quantity_point{123 * m}.unit == si::metre);
|
||||
static_assert(quantity_point{123 * m}.quantity_spec == kind_of<isq::length>);
|
||||
|
||||
static_assert(std::is_same_v<decltype(quantity_point{isq::height(123 * m)})::rep, int>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{isq::height(123 * m)}.point_origin)>,
|
||||
zeroth_point_origin_<isq::height>>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{isq::height(123 * m)}.absolute_point_origin)>,
|
||||
zeroth_point_origin_<isq::height>>);
|
||||
static_assert(quantity_point{isq::height(123 * m)}.unit == si::metre);
|
||||
static_assert(quantity_point{isq::height(123 * m)}.quantity_spec == isq::height);
|
||||
|
||||
static_assert(std::is_same_v<decltype(quantity_point{20 * deg_C})::rep, int>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{20 * deg_C}.point_origin)>,
|
||||
struct si::zeroth_degree_Celsius>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{20 * deg_C}.absolute_point_origin)>,
|
||||
struct si::zeroth_kelvin>);
|
||||
static_assert(quantity_point{20 * deg_C}.unit == si::degree_Celsius);
|
||||
static_assert(quantity_point{20 * deg_C}.quantity_spec == kind_of<isq::thermodynamic_temperature>);
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
static_assert(std::is_same_v<decltype(quantity_point{sys_seconds{123s}})::rep, std::chrono::seconds::rep>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{sys_seconds{123s}}.point_origin)>,
|
||||
chrono_point_origin_<std::chrono::system_clock>>);
|
||||
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{sys_seconds{123s}}.absolute_point_origin)>,
|
||||
chrono_point_origin_<std::chrono::system_clock>>);
|
||||
static_assert(quantity_point{sys_seconds{24h}}.unit == si::second);
|
||||
static_assert(quantity_point{sys_seconds{24h}}.quantity_spec == kind_of<isq::time>);
|
||||
|
||||
|
||||
// ////////////
|
||||
// // getters
|
||||
// ////////////
|
||||
////////////
|
||||
// getters
|
||||
////////////
|
||||
|
||||
constexpr quantity_point mean_sea_level_qp = mean_sea_level + 1 * m;
|
||||
constexpr quantity_point my_mean_sea_level_qp = my_mean_sea_level + 1 * m;
|
||||
@@ -744,14 +979,14 @@ static_assert([](auto v) {
|
||||
////////////////////////
|
||||
|
||||
// same type
|
||||
static_assert((mean_sea_level + 1 * m += 1 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 2);
|
||||
static_assert((mean_sea_level + 2 * m -= 1 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 1);
|
||||
static_assert((mean_sea_level + 1 * m += 1 * m).quantity_from_zero().numerical_value_in(m) == 2);
|
||||
static_assert((mean_sea_level + 2 * m -= 1 * m).quantity_from_zero().numerical_value_in(m) == 1);
|
||||
|
||||
// different types
|
||||
static_assert((mean_sea_level + 2.5 * m += 3 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 5.5);
|
||||
static_assert((mean_sea_level + 123 * m += 1 * km).quantity_from(mean_sea_level).numerical_value_in(m) == 1123);
|
||||
static_assert((mean_sea_level + 5.5 * m -= 3 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 2.5);
|
||||
static_assert((mean_sea_level + 1123 * m -= 1 * km).quantity_from(mean_sea_level).numerical_value_in(m) == 123);
|
||||
static_assert((mean_sea_level + 2.5 * m += 3 * m).quantity_from_zero().numerical_value_in(m) == 5.5);
|
||||
static_assert((mean_sea_level + 123 * m += 1 * km).quantity_from_zero().numerical_value_in(m) == 1123);
|
||||
static_assert((mean_sea_level + 5.5 * m -= 3 * m).quantity_from_zero().numerical_value_in(m) == 2.5);
|
||||
static_assert((mean_sea_level + 1123 * m -= 1 * km).quantity_from_zero().numerical_value_in(m) == 123);
|
||||
|
||||
|
||||
template<template<auto, auto, typename> typename QP>
|
||||
@@ -792,12 +1027,32 @@ concept invalid_binary_operations = requires {
|
||||
requires !requires { isq::length(1 * m) + QP<si::metre, mean_sea_level, int>(1 * m); };
|
||||
requires !requires { QP<isq::height[m], mean_sea_level, int>(1 * m) + isq::length(1 * m); };
|
||||
requires !requires { isq::length(1 * m) + QP<isq::height[m], mean_sea_level, int>(1 * m); };
|
||||
requires !requires { QP<si::metre, zeroth_point_origin<isq::height>, int>(1 * m) + isq::length(1 * m); };
|
||||
requires !requires { isq::length(1 * m) + QP<si::metre, zeroth_point_origin<isq::height>, int>(1 * m); };
|
||||
requires !requires { QP<isq::height[m], zeroth_point_origin<isq::height>, int>(1 * m) + isq::length(1 * m); };
|
||||
requires !requires { isq::length(1 * m) + QP<isq::height[m], zeroth_point_origin<isq::height>, int>(1 * m); };
|
||||
requires !requires { Origin + isq::length(1 * m); };
|
||||
requires !requires { isq::length(1 * m) + Origin; };
|
||||
|
||||
// can't subtract more generic quantity (violates point_origin quantity_spec)
|
||||
requires !requires { QP<si::metre, mean_sea_level, int>(1 * m) - isq::length(1 * m); };
|
||||
requires !requires { QP<isq::height[m], mean_sea_level, int>(1 * m) - isq::length(1 * m); };
|
||||
requires !requires { QP<si::metre, zeroth_point_origin<isq::height>, int>(1 * m) - isq::length(1 * m); };
|
||||
requires !requires { QP<isq::height[m], zeroth_point_origin<isq::height>, int>(1 * m) - isq::length(1 * m); };
|
||||
requires !requires {
|
||||
QP<isq::height[m] / isq::time[s], zeroth_point_origin<isq::height / isq::time>, int>(10 * isq::height[m] /
|
||||
(2 * isq::time[s])) +
|
||||
5 * isq::speed[m / s];
|
||||
};
|
||||
requires !requires {
|
||||
5 * isq::speed[m / s] + QP<isq::height[m] / isq::time[s], zeroth_point_origin<isq::height / isq::time>, int>(
|
||||
10 * isq::height[m] / (2 * isq::time[s]));
|
||||
};
|
||||
requires !requires {
|
||||
QP<isq::height[m] / isq::time[s], zeroth_point_origin<isq::height / isq::time>, int>(10 * isq::height[m] /
|
||||
(2 * isq::time[s])) -
|
||||
5 * isq::speed[m / s];
|
||||
};
|
||||
requires !requires { Origin - isq::length(1 * m); };
|
||||
|
||||
// quantity point can't be subtracted from a quantity
|
||||
@@ -819,6 +1074,28 @@ concept invalid_binary_operations = requires {
|
||||
};
|
||||
requires !requires { mean_sea_level - QP<isq::height[m], other_absolute_level, int>(1 * m); };
|
||||
requires !requires { QP<isq::height[m], mean_sea_level, int>(1 * m) - other_absolute_level; };
|
||||
requires !requires { QP<isq::height[m], mean_sea_level, int>(1 * m) - quantity_point{1 * m}; };
|
||||
requires !requires { quantity_point{1 * m} - QP<isq::height[m], mean_sea_level, int>(1 * m); };
|
||||
requires !requires {
|
||||
QP<isq::width[m], zeroth_point_origin<isq::width>, int>(1 * m) - quantity_point{isq::height(1 * m)};
|
||||
};
|
||||
requires !requires {
|
||||
quantity_point{isq::height(1 * m)} - QP<isq::width[m], zeroth_point_origin<isq::width>, int>(1 * m);
|
||||
};
|
||||
requires !requires {
|
||||
QP<isq::width[m], zeroth_point_origin<isq::width>, int>(1 * m) - quantity_point{isq::length(1 * m)};
|
||||
};
|
||||
requires !requires {
|
||||
quantity_point{isq::length(1 * m)} - QP<isq::width[m], zeroth_point_origin<isq::width>, int>(1 * m);
|
||||
};
|
||||
requires !requires {
|
||||
quantity_point{10 * isq::height[m] / (2 * isq::time[s])} -
|
||||
QP<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>(5 * isq::speed[m / s]);
|
||||
};
|
||||
requires !requires {
|
||||
QP<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>(5 * isq::speed[m / s]) -
|
||||
quantity_point{10 * isq::height[m] / (2 * isq::time[s])};
|
||||
};
|
||||
|
||||
// cant'subtract two unrelated points
|
||||
requires !requires { Origin - OtherOrigin; };
|
||||
@@ -1244,6 +1521,50 @@ static_assert(
|
||||
is_of_type<(zero_m_per_s + 10 * isq::height[m] / (2 * isq::time[s])) + (10 * isq::height[m] / (2 * isq::time[s])),
|
||||
quantity_point<(isq::height / isq::time)[m / s], zero_m_per_s, int>>);
|
||||
|
||||
static_assert((quantity_point{5 * isq::speed[m / s]} + 10 * isq::length[m] / (2 * isq::time[s])).quantity_from_zero() ==
|
||||
10 * isq::speed[m / s]);
|
||||
static_assert((10 * isq::length[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}).quantity_from_zero() ==
|
||||
10 * isq::speed[m / s]);
|
||||
static_assert((quantity_point{5 * isq::speed[m / s]} - 10 * isq::length[m] / (2 * isq::time[s])).quantity_from_zero() ==
|
||||
0 * isq::speed[m / s]);
|
||||
|
||||
static_assert((quantity_point{10 * isq::length[m] / (2 * isq::time[s])} + 5 * isq::speed[m / s]).quantity_from_zero() ==
|
||||
10 * isq::speed[m / s]);
|
||||
static_assert((5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}).quantity_from_zero() ==
|
||||
10 * isq::speed[m / s]);
|
||||
static_assert((quantity_point{10 * isq::length[m] / (2 * isq::time[s])} - 5 * isq::speed[m / s]).quantity_from_zero() ==
|
||||
0 * isq::speed[m / s]);
|
||||
|
||||
static_assert((quantity_point{5 * isq::speed[m / s]} + 10 * isq::height[m] / (2 * isq::time[s])).quantity_from_zero() ==
|
||||
10 * isq::speed[m / s]);
|
||||
static_assert((10 * isq::height[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}).quantity_from_zero() ==
|
||||
10 * isq::speed[m / s]);
|
||||
static_assert((quantity_point{5 * isq::speed[m / s]} - 10 * isq::height[m] / (2 * isq::time[s])).quantity_from_zero() ==
|
||||
0 * isq::speed[m / s]);
|
||||
|
||||
static_assert(is_of_type<quantity_point{10 * isq::length[m] / (2 * isq::time[s])} + 5 * isq::speed[m / s],
|
||||
quantity_point<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>>);
|
||||
static_assert(is_of_type<10 * isq::height[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]},
|
||||
quantity_point<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>>);
|
||||
static_assert(is_of_type<quantity_point{5 * isq::speed[m / s]} + 10 * isq::height[m] / (2 * isq::time[s]),
|
||||
quantity_point<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>>);
|
||||
static_assert(is_of_type<5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])},
|
||||
quantity_point<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>>);
|
||||
static_assert(is_of_type<quantity_point{10 * isq::length[m] / (2 * isq::time[s])} - 5 * isq::speed[m / s],
|
||||
quantity_point<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>>);
|
||||
static_assert(is_of_type<quantity_point{5 * isq::speed[m / s]} - 10 * isq::height[m] / (2 * isq::time[s]),
|
||||
quantity_point<isq::speed[m / s], zeroth_point_origin<isq::speed>, int>>);
|
||||
static_assert(
|
||||
is_of_type<quantity_point{10 * isq::length[m] / (2 * isq::time[s])} - quantity_point{5 * isq::speed[m / s]},
|
||||
quantity<isq::speed[m / s], int>>);
|
||||
static_assert(
|
||||
is_of_type<quantity_point{5 * isq::speed[m / s]} - quantity_point{10 * isq::length[m] / (2 * isq::time[s])},
|
||||
quantity<isq::speed[m / s], int>>);
|
||||
|
||||
static_assert(
|
||||
is_of_type<quantity_point{10 * isq::height[m] / (2 * isq::time[s])} + (10 * isq::height[m] / (2 * isq::time[s])),
|
||||
quantity_point<(isq::height / isq::time)[m / s], zeroth_point_origin<isq::height / isq::time>, int>>);
|
||||
|
||||
inline constexpr struct zero_Hz : absolute_point_origin<zero_Hz, kind_of<isq::frequency>> {
|
||||
} zero_Hz;
|
||||
|
||||
@@ -1281,6 +1602,40 @@ static_assert(is_of_type<(zero_Hz + 10 / (2 * isq::period_duration[s])) - (zero_
|
||||
static_assert(is_of_type<(zero_Hz + 5 * isq::frequency[Hz]) - (zero_Hz + 10 / (2 * isq::period_duration[s])),
|
||||
quantity<isq::frequency[Hz], int>>);
|
||||
|
||||
static_assert((quantity_point{10 / (2 * isq::period_duration[s])} + 5 * isq::frequency[Hz]).quantity_from_zero() ==
|
||||
10 * isq::frequency[Hz]);
|
||||
static_assert((10 / (2 * isq::period_duration[s]) + quantity_point{zero_Hz + 5 * isq::frequency[Hz]})
|
||||
.quantity_from_zero() == 10 * isq::frequency[Hz]);
|
||||
static_assert((quantity_point{5 * isq::frequency[Hz]} + 10 / (2 * isq::period_duration[s])).quantity_from_zero() ==
|
||||
10 * isq::frequency[Hz]);
|
||||
static_assert((5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}).quantity_from_zero() ==
|
||||
10 * isq::frequency[Hz]);
|
||||
static_assert((quantity_point{10 / (2 * isq::period_duration[s])} - 5 * isq::frequency[Hz]).quantity_from_zero() ==
|
||||
0 * isq::frequency[Hz]);
|
||||
static_assert((quantity_point{5 * isq::frequency[Hz]} - 10 / (2 * isq::period_duration[s])).quantity_from_zero() ==
|
||||
0 * isq::frequency[Hz]);
|
||||
static_assert(quantity_point{10 / (2 * isq::period_duration[s])} - quantity_point{5 * isq::frequency[Hz]} ==
|
||||
0 * isq::frequency[Hz]);
|
||||
static_assert(quantity_point{5 * isq::frequency[Hz]} - quantity_point{10 / (2 * isq::period_duration[s])} ==
|
||||
0 * isq::frequency[Hz]);
|
||||
|
||||
static_assert(is_of_type<quantity_point{10 / (2 * isq::period_duration[s])} + 5 * isq::frequency[Hz],
|
||||
quantity_point<isq::frequency[Hz], zeroth_point_origin<isq::frequency>, int>>);
|
||||
static_assert(is_of_type<10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]},
|
||||
quantity_point<isq::frequency[Hz], zeroth_point_origin<isq::frequency>, int>>);
|
||||
static_assert(is_of_type<quantity_point{5 * isq::frequency[Hz]} + 10 / (2 * isq::period_duration[s]),
|
||||
quantity_point<isq::frequency[Hz], zeroth_point_origin<isq::frequency>, int>>);
|
||||
static_assert(is_of_type<5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])},
|
||||
quantity_point<isq::frequency[Hz], zeroth_point_origin<isq::frequency>, int>>);
|
||||
static_assert(is_of_type<quantity_point{10 / (2 * isq::period_duration[s])} - 5 * isq::frequency[Hz],
|
||||
quantity_point<isq::frequency[Hz], zeroth_point_origin<isq::frequency>, int>>);
|
||||
static_assert(is_of_type<quantity_point{5 * isq::frequency[Hz]} - 10 / (2 * isq::period_duration[s]),
|
||||
quantity_point<isq::frequency[Hz], zeroth_point_origin<isq::frequency>, int>>);
|
||||
static_assert(is_of_type<quantity_point{10 / (2 * isq::period_duration[s])} - quantity_point{5 * isq::frequency[Hz]},
|
||||
quantity<isq::frequency[Hz], int>>);
|
||||
static_assert(is_of_type<quantity_point{5 * isq::frequency[Hz]} - quantity_point{10 / (2 * isq::period_duration[s])},
|
||||
quantity<isq::frequency[Hz], int>>);
|
||||
|
||||
// Different named dimensions
|
||||
template<typename... Ts>
|
||||
consteval bool invalid_addition(Ts... ts)
|
||||
@@ -1307,4 +1662,16 @@ static_assert(invalid_addition(5 * isq::activity[Bq], zero_Hz + 10 / (2 * isq::t
|
||||
static_assert(invalid_addition(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), zero_Hz + 5 * isq::frequency[Hz]));
|
||||
static_assert(invalid_subtraction(zero_Bq + 5 * isq::activity[Bq], 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz]));
|
||||
|
||||
static_assert(invalid_addition(quantity_point{5 * isq::activity[Bq]}, 5 * isq::frequency[Hz]));
|
||||
static_assert(invalid_addition(5 * isq::activity[Bq], quantity_point{5 * isq::frequency[Hz]}));
|
||||
static_assert(invalid_subtraction(quantity_point{5 * isq::activity[Bq]}, 5 * isq::frequency[Hz]));
|
||||
static_assert(invalid_subtraction(quantity_point{5 * isq::activity[Bq]}, quantity_point{5 * isq::frequency[Hz]}));
|
||||
|
||||
static_assert(invalid_addition(quantity_point{5 * isq::activity[Bq]}, 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz]));
|
||||
static_assert(invalid_addition(5 * isq::activity[Bq], quantity_point{10 / (2 * isq::time[s])}, 5 * isq::frequency[Hz]));
|
||||
static_assert(invalid_addition(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), quantity_point{5 * isq::frequency[Hz]}));
|
||||
static_assert(invalid_subtraction(quantity_point{5 * isq::activity[Bq]}, 10 / (2 * isq::time[s]),
|
||||
5 * isq::frequency[Hz]));
|
||||
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user