2024-10-27 15:34:18 +01:00
|
|
|
|
---
|
2024-11-05 17:25:45 +01:00
|
|
|
|
date: 2024-11-05
|
2024-10-27 15:34:18 +01:00
|
|
|
|
authors:
|
|
|
|
|
|
- mpusz
|
|
|
|
|
|
categories:
|
|
|
|
|
|
- Releases
|
|
|
|
|
|
comments: true
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# mp-units 2.4.0 released!
|
|
|
|
|
|
|
|
|
|
|
|
**A new product version can be obtained from
|
|
|
|
|
|
[GitHub](https://github.com/mpusz/mp-units/releases/tag/v2.4.0) and
|
|
|
|
|
|
[Conan](https://conan.io/center/recipes/mp-units?version=2.4.0).**
|
|
|
|
|
|
|
|
|
|
|
|
This release was unexpected. We planned a significant new feature to happen next, but while
|
|
|
|
|
|
preparing for it, and also while writing API Reference documentation, we made so many vital fixes
|
|
|
|
|
|
and improvements that we decided that they deserve a dedicated release first.
|
|
|
|
|
|
|
2024-11-05 17:25:45 +01:00
|
|
|
|
This post describes the most significant improvements while a much longer list of the changes
|
2024-10-27 15:34:18 +01:00
|
|
|
|
introduced by the new version can be found in our [Release Notes](../../release_notes.md#2.4.0).
|
|
|
|
|
|
|
|
|
|
|
|
<!-- more -->
|
|
|
|
|
|
|
|
|
|
|
|
## ISQ quantities cleanup
|
|
|
|
|
|
|
|
|
|
|
|
Initially, we kept quantities defined in "IEC 80000-13: Information science and technology" in
|
|
|
|
|
|
a standalone `iec80000` namespace, which was renamed to `iec` in the previous release.
|
|
|
|
|
|
It turned out that this was incorrect. Those quantities are also a part of the ISQ. This is why,
|
|
|
|
|
|
in this release, we moved all of them to the `isq` namespace (:boom: **breaking change** :boom:).
|
|
|
|
|
|
|
|
|
|
|
|
From now on, `iec` namespace does not provide any quantities and serves purely as a system of units
|
|
|
|
|
|
definition. It contains binary prefixes (based on the powers of two) and some units introduced
|
2024-11-05 17:25:45 +01:00
|
|
|
|
by IEC (e.g., `var`, `erlang`, `bit`, or ``baud`).
|
2024-10-27 15:34:18 +01:00
|
|
|
|
|
|
|
|
|
|
!!! note
|
|
|
|
|
|
|
|
|
|
|
|
The quantities in `iec` namespace are now deprecated and will be removed in future releases.
|
|
|
|
|
|
|
|
|
|
|
|
Also, it turns out that the latest ISO 80000-3 revision makes a small cleanup to the `phase_speed`
|
|
|
|
|
|
and `group_speed` quantities. Those were always defined as scalar quantities but also had
|
2024-11-05 17:25:45 +01:00
|
|
|
|
alternative names `phase_velocity` and `group_velocity`. This is misleading as _velocity_ is
|
|
|
|
|
|
typically considered a vector quantity. It is why those `XXX_velocity` aliases were removed from
|
|
|
|
|
|
the ISO standard and from **mp-units** library (:boom: **breaking change** :boom:).
|
2024-10-27 15:34:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Units equality
|
|
|
|
|
|
|
|
|
|
|
|
Previously we assumed that units like `J`, `N m`, and `kg m²/s²` are equal. In some cases,
|
2024-11-05 17:25:45 +01:00
|
|
|
|
this might not be entirely correct. Some quantities require a specific derived unit instead of
|
|
|
|
|
|
a unit with a special name. For example:
|
2024-10-27 15:34:18 +01:00
|
|
|
|
|
|
|
|
|
|
- `N m` should be used for _moment of force_ (instead of `J`),
|
|
|
|
|
|
- `V A` should be used for _apparent power_ (instead of `W`).
|
|
|
|
|
|
|
|
|
|
|
|
This is why, starting from this release units like `J`, `N m`, and `kg m²/s²` will not compare equal
|
|
|
|
|
|
(:boom: **breaking change** :boom:). However, they are deemed equivalent:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
static_assert(equivalent(J, N * m));
|
|
|
|
|
|
static_assert(equivalent(W, V * A));
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Portable text output
|
|
|
|
|
|
|
|
|
|
|
|
From the very beginning, the text output of symbols could be formatted in two different ways:
|
|
|
|
|
|
|
|
|
|
|
|
- Unicode,
|
|
|
|
|
|
- portable using so-called ASCII alternatives.
|
|
|
|
|
|
|
|
|
|
|
|
**mp-units** used the terms "Unicode" or "ASCII" and 'U' or 'A' formatting options for them.
|
|
|
|
|
|
Even though those terms are widely understood in the C++ community, they are technically
|
|
|
|
|
|
incorrect.
|
|
|
|
|
|
|
2024-11-05 17:25:45 +01:00
|
|
|
|
During the recent SG16 meeting, we looked for proper alternatives and ended up with the "portable"
|
|
|
|
|
|
and "UTF-8" terms (:boom: **breaking change** :boom:).
|
2024-10-27 15:34:18 +01:00
|
|
|
|
|
2024-11-05 17:25:45 +01:00
|
|
|
|
From now on, we will provide the following:
|
2024-10-27 15:34:18 +01:00
|
|
|
|
|
|
|
|
|
|
- `text_encoding::utf8`, `symbol_text<N, M>::utf8()`, and `U` formatting option,
|
|
|
|
|
|
- `text_encoding::portable`, `symbol_text<N, M>::portable()`, and `P` formatting option.
|
|
|
|
|
|
|
|
|
|
|
|
!!! note
|
|
|
|
|
|
|
|
|
|
|
|
The old identifiers and formatting options are now deprecated and will be removed in future
|
|
|
|
|
|
releases.
|
|
|
|
|
|
|
2024-11-05 17:25:45 +01:00
|
|
|
|
*[SG16]: WG21 Unicode Study Group
|
|
|
|
|
|
|
2024-10-27 15:34:18 +01:00
|
|
|
|
|
|
|
|
|
|
## `char_traits` removed from `fixed_string`
|
|
|
|
|
|
|
|
|
|
|
|
During the same SG16 meeting, the room was strongly against providing `char_traits` for
|
|
|
|
|
|
`fixed_string`. This is why `char_traits` support was removed in this release
|
|
|
|
|
|
(:boom: **breaking change** :boom:).
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-05 17:25:45 +01:00
|
|
|
|
## Improved units' text output
|
2024-10-27 15:34:18 +01:00
|
|
|
|
|
|
|
|
|
|
In the previous release, we introduced common unit abstraction. Initially, all its components
|
|
|
|
|
|
were printed in parenthesis which contained a list of all the scaled units separated with `=`.
|
|
|
|
|
|
After some feedback, we decided to change it to a new syntax.
|
|
|
|
|
|
|
|
|
|
|
|
For example, the following:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
std::cout << 1 * km + 1 * mi << "\n";
|
|
|
|
|
|
std::cout << 1 * nmi + 1 * mi << "\n";
|
|
|
|
|
|
std::cout << 1 * km / h + 1 * m / s << "\n";
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
will print:
|
|
|
|
|
|
|
|
|
|
|
|
=== "Now"
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
40771 EQUIV{[1/25146 mi], [1/15625 km]}
|
|
|
|
|
|
108167 EQUIV{[1/50292 mi], [1/57875 nmi]}
|
|
|
|
|
|
23 EQUIV{[1/5 km/h], [1/18 m/s]}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
=== "Before"
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
40771 ([1/25146] mi = [1/15625] km)
|
|
|
|
|
|
108167 ([1/50292] mi = [1/57875] nmi)
|
|
|
|
|
|
23 ([1/5] km/h = [1/18] m/s)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
As we can see above, the scaled units output changed as well. Now, the entire scaled unit is
|
|
|
|
|
|
encapsulated within `[...]` brackets to better denote its scope.
|
|
|
|
|
|
|
|
|
|
|
|
Additionally, small magnitudes of scaled units do not use the power of `10`, and also scaled
|
|
|
|
|
|
units do not have a composition priority over the derived units anymore.
|
|
|
|
|
|
|
|
|
|
|
|
As a result of those changes, the following:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
constexpr Unit auto L_per_100km = L / (mag<100> * km);
|
|
|
|
|
|
std::cout << 6.7 * L_per_100km << "\n";
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
prints:
|
|
|
|
|
|
|
|
|
|
|
|
=== "Now"
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
6.7 L/[100 km]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
=== "Before"
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
6.7 × 10⁻² l/km
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2024-11-05 17:25:45 +01:00
|
|
|
|
One more change that we can see above is that litre now use 'L' instead of 'l' for its symbol.
|
2024-10-27 15:34:18 +01:00
|
|
|
|
The latter one too often is confused with the number `1`.
|
|
|
|
|
|
|
|
|
|
|
|
The next improvement adds proper formatting support for magnitudes. All of the formatting options
|
|
|
|
|
|
that were working before for the unit symbols now also work for magnitudes.
|
|
|
|
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
using enum text_encoding;
|
|
|
|
|
|
using enum unit_symbol_solidus;
|
|
|
|
|
|
using usf = unit_symbol_formatting;
|
|
|
|
|
|
|
|
|
|
|
|
static_assert(unit_symbol(mag<1> / (mag<2> * mag<pi>)*metre) == "[2⁻¹ 𝜋⁻¹ m]");
|
|
|
|
|
|
static_assert(unit_symbol<usf{.solidus = always}>(mag<1> / (mag<2> * mag<pi>)*metre) == "[1/(2 𝜋) m]");
|
|
|
|
|
|
static_assert(unit_symbol<usf{.encoding = portable, .solidus = always}>(mag<1> / (mag<2> * mag<pi>)*metre) ==
|
|
|
|
|
|
"[1/(2 pi) m]");
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
As we can see above, the library also learned how to print magnitude symbols. This required
|
|
|
|
|
|
a change in the `mag_constant` definition. Now, it takes a magnitude symbol and has to be `final`
|
|
|
|
|
|
like for other similar types in the library (:boom: **breaking change** :boom:):
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
inline constexpr struct pi final : mag_constant<symbol_text{u8"π", "pi"}, std::numbers::pi_v<long double>> {} pi;
|
|
|
|
|
|
inline constexpr auto π = pi;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Unicode identifiers
|
|
|
|
|
|
|
|
|
|
|
|
The example above introduced something interesting: a `π` identifier for a variable. With the
|
|
|
|
|
|
latest changes to the C++ language, we can officially use Unicode symbols as identifiers in
|
|
|
|
|
|
the C++ code.
|
|
|
|
|
|
|
2024-11-05 17:25:45 +01:00
|
|
|
|
In this release, we've added Unicode identifiers support not only for `π` magnitude constant
|
|
|
|
|
|
but also for unit symbols.
|
|
|
|
|
|
|
2024-10-27 15:34:18 +01:00
|
|
|
|
Now we can type the following:
|
|
|
|
|
|
|
|
|
|
|
|
=== "With UTF-8 glyphs"
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
quantity resistance = 60 * kΩ;
|
|
|
|
|
|
quantity capacitance = 100 * µF;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
=== "Portable"
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
quantity resistance = 60 * kohm;
|
|
|
|
|
|
quantity capacitance = 100 * uF;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
This might make the source code easier to understand, but typing those identifiers can be tricky.
|
|
|
|
|
|
Sometimes, the best solution to type it might be a copy-paste approach. If we do not like this
|
|
|
|
|
|
idea, we can still use old portable identifiers for those as well.
|
2024-11-05 17:25:45 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Convertibility with `QuantityLike` and `QuantityPointLike` entities
|
|
|
|
|
|
|
|
|
|
|
|
In this release, we decided to fine-tune further the traits that customize the conversion between
|
|
|
|
|
|
custom quantity and quantity point types and the ones provided with **mp-units**
|
|
|
|
|
|
(:boom: **breaking change** :boom:).
|
|
|
|
|
|
|
|
|
|
|
|
Previously, `to_numerical_value` and `from_numerical_value` returned a type wrapped in a special
|
|
|
|
|
|
tag type describing the conversion type (explicit or implicit).
|
|
|
|
|
|
|
|
|
|
|
|
This was a novel and experimental approach. Finally, we decided not to do it and used a bit more
|
|
|
|
|
|
verbose but a more standard solution. From now on, we need to provide two additional static data
|
|
|
|
|
|
members of type `bool`:
|
|
|
|
|
|
|
|
|
|
|
|
- `explicit_import` - `true` means that the conversion to the **mp-units** abstraction is explicit,
|
|
|
|
|
|
- `explicit_export` - `true` means that the conversion from the **mp-units** abstraction is
|
|
|
|
|
|
explicit.
|
|
|
|
|
|
|
|
|
|
|
|
=== "Now"
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
template<>
|
|
|
|
|
|
struct mp_units::quantity_point_like_traits<Timestamp> {
|
|
|
|
|
|
static constexpr auto reference = si::second;
|
|
|
|
|
|
static constexpr auto point_origin = default_point_origin(reference);
|
|
|
|
|
|
static constexpr bool explicit_import = false;
|
|
|
|
|
|
static constexpr bool explicit_export = true;
|
|
|
|
|
|
using rep = decltype(Timestamp::seconds);
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr rep to_numerical_value(Timestamp ts)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ts.seconds;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr Timestamp from_numerical_value(rep v)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Timestamp(v);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
=== "Before"
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
template<>
|
|
|
|
|
|
struct mp_units::quantity_point_like_traits<Timestamp> {
|
|
|
|
|
|
static constexpr auto reference = si::second;
|
|
|
|
|
|
static constexpr auto point_origin = default_point_origin(reference);
|
|
|
|
|
|
using rep = decltype(Timestamp::seconds);
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr convert_implicitly<rep> to_numerical_value(Timestamp ts)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ts.seconds;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr convert_explicitly<Timestamp> from_numerical_value(rep v)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Timestamp(v);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Symbolic constants implementation should be _implementation-defined_
|
|
|
|
|
|
|
|
|
|
|
|
In the process of writing API Reference, we decided to hide all the metadata associated with
|
|
|
|
|
|
symbolic constants - tag types used to define units, dimensions, quantity specification, etc.
|
|
|
|
|
|
(:boom: **breaking change** :boom:).
|
|
|
|
|
|
|
|
|
|
|
|
All the types and values exposed by such types are needed only in the implementation details
|
|
|
|
|
|
of the library. Users should not need them. Hiding those and making them
|
|
|
|
|
|
_implementation-defined_ gives other vendors the freedom to choose different ways to implement
|
|
|
|
|
|
features of this library in their codebases.
|
|
|
|
|
|
|
|
|
|
|
|
!!! important
|
|
|
|
|
|
|
|
|
|
|
|
Based on [Hyrum's Law](https://www.hyrumslaw.com/) some users may depend on this
|
|
|
|
|
|
information already, and this release will break their code.
|
|
|
|
|
|
|
|
|
|
|
|
If that is the case for you, we would love to hear about your use case and its rationale.
|
|
|
|
|
|
It may mean that we should either:
|
|
|
|
|
|
|
|
|
|
|
|
- extend the library's functionality to support your use case out of the box and keep those
|
|
|
|
|
|
members hidden,
|
|
|
|
|
|
- restore public visibility of such members and enforce this in the API Reference so that all
|
|
|
|
|
|
the users of various library implementations may use them in the same way as you.
|