Skip to content

mp-units 2.1.0 released!

A new product version can be obtained from GitHub and Conan.

The list of the most significant changes introduced by the new version can be found in our Release Notes. We will also describe the most important of them in this post.

No more parenthesis while creating quantities with derived units

The V2 design introduced a way to create a quantity by multiplying a raw value and a unit:

quantity q1 = 42 * m;

However, this meant that when we wanted to create a quantity having a derived unit, we had to put parenthesis around the unit equation or create a custom value of the named unit:

quantity q2 = 60 * (km / h);

constexpr auto kmph = km / h;
quantity q3 = 60 * kmph;

quantity q4 = 50 * (1 / s);

With the new version, we removed this restriction, and now we can type:

quantity q5 = 60 * km / h;
quantity q6 = 50 / s;

As a side effect, we introduced a breaking change. We can't use the following definition of hertz anymore:

inline constexpr struct hertz : named_unit<"Hz", 1 / second, kind_of<isq::frequency>> {} hertz;

and have to type either:

inline constexpr struct hertz : named_unit<"Hz", one / second, kind_of<isq::frequency>> {} hertz;

or

inline constexpr struct hertz : named_unit<"Hz", inverse(second), kind_of<isq::frequency>> {} hertz;

To be consistent, we applied the same change to the dimensions and quantity specifications definitions. Now, to define a frequency we have to type:

inline constexpr struct frequency : quantity_spec<inverse(period_duration)> {} frequency;
inline constexpr struct frequency : quantity_spec<frequency, inverse(period_duration)> {} frequency;
QUANTITY_SPEC(frequency, inverse(period_duration));

make_xxx factory functions replaced with two-parameter constructors

In the initial version of the V2 framework, if someone did not like the multiply syntax to create a quantity we provided the make_quantity() factory function. A similar approach was used for quantity_point creation.

This version removes those (breaking change) and introduces two parameter constructors:

quantity q(42, si::metre);
quantity_point qp(q, mean_sea_level);

The above change encourages a better design and results in a terser code.

Improved definitions of becquerel, gray, and sievert

In the initial V2 version, we lacked the definitions of the atomic and nuclear physics quantities, which resulted in simplified and unsafe definitions of becquerel, gray, and sievert units. We still do not model most of the quantities from this domain, but we've added the ones that are necessary for the definition of those units.

Thanks to the above, the following expressions will not compile:

quantity q1 = 1 * Hz + 1 * Bq;
quantity<si::sievert> q2 = 42 * Gy;

Compatibility with other libraries redesigned

Another significant improvement in this version was redesigning the way we provide compatibility with other similar libraries. The interfaces of quantity_like_traits and quantity_point_like_traits were changed and extended to provide conversion not only from but also to entities from other libraries (breaking change).

We've also introduced an innovative approach that allows us to specify if such conversions should happen implicitly or if they need to be forced explicitly.

More on this subject can be found in the Interoperability with Other Libraries chapter.

Point origins can now be derived from each other

Previously, each class derived from absolute_point_origin was considered a unique independent point origin. On the other hand, it was OK to derive multiple classes from the same relative_point_origin, and those were specifying the same point in the domain. We found this confusing and limiting. This is why, in this version, the absolute_point_origin uses a CRTP idiom to be able to detect between points that should be considered different from the ones that should be equivalent.

If we derive from the same instantiation of absolute_point_origin we end up with an equivalent point origin. This change allows us to provide different names for the same temperature points:

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;

Please note that this is a breaking change as well.

Unit symbol text can now be properly used at runtime

The interface of the previous definition of unit_symbol function allowed the use of the returned buffer only at compile-time. This was too limiting as users often want to use unit symbols at runtime (e.g., print them to the console). The new version redesigned the interface of this function (breaking change) to return a buffer that can be properly used at both compilation and runtime:

std::string_view unit1 = unit_symbol(m / s);
std::cout << unit1 << "\n";     // m/s
std::string_view unit2 = unit_symbol<{.solidus = unit_symbol_solidus::never}>(m / s);
std::cout << unit2 << "\n";     // m s⁻¹