mp-units 2.5.0 released¶
A new product version can be obtained from GitHub and Conan.
I initially had different plans for this release, but during development it turned out that the new big feature I was working on was too big and will require some breaking changes. This put me off tracks, and unfortunately the mp-units development slowed down recently 😞
It also turned out that I got unemployed, so now I depend solely on my C++ trainer's career to earn money for living. Fortunately, I delivered many in-house C++ trainings to my customers this year, and I hope this will also be the case for the upcoming 2026. I was also busy with preparations of new talks and classes for this year's C++ conferences.
Having that much on my plate, a break from mp-units was needed to not burn out on the way. But hopefully I am back now and I plan to continue working on long awaited features now.
Said that, if you care about mp-units and would like to see it grow faster, please either contribute or consider sponsoring my work.
This release contains a lot of small patches and improvements.
This post describes the most significant changes while a much longer list of the changes introduced by the new version can be found in our Release Notes.
Representation concepts improved¶
First, handling of custom representation types has been changed. We no longer have to
specialize is_scalar, is_vector, and is_tensor customization points for custom
representation types. Their character will be deduced from the types interface now.
To enable detection of the following representation categories we provided new CPOs:
-
ComplexScalarrequires all of the bellow:real- CPO that checks for member or non-member functionreal(),imag- CPO that checks for member or non-member functionimag(),modulus- CPO that checks for member or non-member functionsmodulus()orabs()(to supportstd::complex).
-
RealScalaris the types that provide scalar like interface and do not satisfyComplexScalarconcept-
Additionally any type that accidentally provides such interfaces, but should not be used as a real scalar quantity representation, may opt-off from this concept by specializing the
disable_real<>customization point.For example, we are doing this for a
booltype already:
-
-
Vectorrequires the type to be compatible with themagnitudeCPO which checks for member or non-member functionmagnitude(). Moreover, to allow using real types to represent one-dimensional vector quantities it also checks if the type satisfiesRealScalarconcept and:- if the type satisfies
std::is_arithmeticit usesstd::abs(), - otherwise, it looks for member or non-member function
abs().
- if the type satisfies
We've also removed the Representation concept ( breaking change
). It turned out to not be that useful.
Additionally, RepresentationOf concept now takes QuantitySpec (instead of
quantity_character) ( breaking change
) which allowed us to
accept representation type of any character for quantity kinds (when just unit
is used and no specific
quantity_spec is provided). For example, this allows
us the following:
// quantity kind (any quantity type of kind length)
quantity q1 = 10 * m;
quantity q2 = cartesian_vector(1, 2, 3) * m;
// vector quantity
quantity q3 = isq::displacement(10 * m);
quantity q4 = isq::displacement(cartesian_vector(1, 2, 3) * m);
// scalar quantity
quantity q5 = isq::width(10 * m);
// quantity q6 = isq::width(cartesian_vector(1, 2, 3) * m); // Compile-time error
As we can see above, in this release we are allowed to use a vector
representation type for q2 as we do not know a specific type of this quantity.
We just know that it is some kind of length. Using a vector quantity for q6
is not allowed as isq::width is defined as a scalar quantity.
On the other hand, this release also allows us to use scalar types for vector
quantities, so in case of q3 we can use int for isq::displacement even
though it is defined as a vector quantity. In this case an int is considered
a one-dimensional vector type.
Info
To enable vector quantities usage we have added a very simple
cartesian_vector representation type in this release.
std::numeric_limits support added¶
This release introduces proper std::numeric_limits specialization support for
quantity and quantity_point types.
The default implementation delegates to the numerical properties of the underlying
representation type, making it easier to integrate mp-units with generic
numerical code that relies on std::numeric_limits.
Automatic SI prefix selection with si::invoke_with_prefixed¶
A new utility function si::invoke_with_prefixed has been added to automatically
select the most appropriate SI prefix for quantity values across wide ranges:
quantity voltage = 0.001'234 * V;
si::invoke_with_prefixed([](auto q) { std::cout << q << '\n'; }, voltage, V);
// Prints: 1.234 mV
This function is particularly useful when displaying values that may span many orders of magnitude, such as in the capacitor discharge example where voltage decays from volts through millivolts, microvolts, nanovolts to picovolts.
The function supports two modes via the prefix_range parameter:
prefix_range::engineering(default) - selects only powers of 1000, resulting in values in range [1.0, 1000)prefix_range::full- selects all SI prefixes including intermediate ones (deca, hecto, deci, centi), often resulting in values in range [1.0, 10.0)
For more details, see the Systems of Units chapter.
Improved quantity specification conversions¶
The conversion rules for quantity specifications have been refined to better preserve type relationships:
- Subkinds (e.g., angular measure) are no longer implicitly convertible to their
parent kind (e.g., dimensionless) (
breaking change
). This improves type safety by preventing accidental loss of specificity.
- Convertibility of ingredients is now better preserved in derived types, ensuring that dimensional relationships are maintained correctly through operations.
-
Explicit
quantity_specconversions can now additionally be performed through the explicit constructor:
Absolute quantities renamed to quantity points¶
In this release, we've renamed the absolute construction helper to point
( breaking change
). This change better aligns with established
terminology in mathematics and computer graphics where "point" is the standard
term for affine space elements.
Additionally, this release adds mathematical operations for quantity points:
lerp- linear interpolation between two pointsmidpoint- finds the midpoint between two points
quantity_point start = point<deg_C>(20.0);
quantity_point end = point<deg_C>(30.0);
quantity_point mid = midpoint(start, end); // 25 ℃
Quantity characters refinement¶
Several improvements have been made to quantity character handling:
- Character names
scalarandcomplexhave been renamed toreal_scalarandcomplex_scalarrespectively (breaking change
) for better clarity and consistency with mathematical terminology.
- Electromagnetism quantities have been updated to IEC 80000-6:2022 standard.
- Complex characters are now properly applied to all complex electromagnetism
quantities including
electric_current_phasor,voltage_phasor, andcomplex_power.
ISQ tree structure improvements¶
The International System of Quantities (ISQ) tree has been reorganized to better reflect physical relationships:
isq::displacementandisq::position_vectorhave been moved to different positions in the hierarchyisq::velocitydefinition has been corrected to useisq::displacementinstead ofisq::position_vector, properly reflecting that velocity is the rate of change of displacement
These changes improve the semantic correctness of quantity relationships within the ISQ framework.
quantity::one() removed¶
The quantity::one() member function has been removed ( breaking change
).
This function was not truly an identity element (it only worked correctly for
dimensionless[one] quantities) and caused confusion.
For creating unit quantities, use representation_values<Rep>::one() multiplied
by the desired unit:
Overflow detection for unit conversions¶
A new scaling_overflows_non_zero_values function has been added to detect at
compile time whether a unit conversion will cause overflow for non-zero values
of the representation type:
static_assert(!scaling_overflows_non_zero_values<int>(si::kilo<si::metre>, si::metre));
static_assert(scaling_overflows_non_zero_values<std::int8_t>(si::kilo<si::metre>, si::metre));
Those are used in quantity value conversion functions and prevent an obvious overflow while changing an unit. This helps identify problematic conversions before runtime and improves code safety.
Enhanced customization points¶
A new is_value_preserving customization point has been added to control
value-preserving conversion semantics for user-defined types:
template<typename From, typename To>
constexpr bool is_value_preserving = treat_as_floating_point<To> || !treat_as_floating_point<From>;
With this user has a fine-grained control to specify if the conversions between two specific types are value-preserving or not.
Info
The current default implementation follows std::chrono::duration approach.
In the future we may replace the current default logic with new type traits.
For example:
Prime factorization improvements¶
Thanks to @chiphogg, the magnitude system can now
prime-factorize any rational magnitude, not just specific cases. This removes the
need for the first_known_factor workaround and makes magnitude arithmetic more
robust and efficient.
value_type_t made recursive¶
The value_type_t type trait now recursively unwraps nested quantity-like types,
making it more useful for generic programming with complex nested structures.
Text encoding improvements¶
The text_encoding enumeration has been renamed to character_set
( breaking change
) to better reflect its purpose. The text output
system now also properly falls back to portable mode when UTF-8 is not being used,
ensuring correct output in more environments.
Improved unit text output¶
The text representation of scaled units has been improved:
- Scaled units are now enclosed in parentheses
(...)instead of brackets[...] - The
EQUIV{...}notation for common units has been replaced with simpler[...]brackets
constexpr Unit auto l_per_100km = si::litre / (mag<100> * si::kilo<si::metre>);
std::cout << 6.8 * l_per_100km << "\n";
std::cout << 1 * km + 1 * mi << "\n";
Breaking changes to naming and organization¶
Several types and functions have been renamed or reorganized for consistency
and clarity ( breaking changes
):
quantity_values→representation_valuesMagnitudeconcept →UnitMagnitudemagnitude→detail::unit_magnitude(moved to implementation details)MagConstantconcept →detail::is_mag_constantvariable traitdefault_denominator→default_solidusin theunit_symbol_solidusenumtype_listhas been moved to implementation detailsunit_symbolanddimension_symbolnow always returnstd::string_viewinstead of various string-like typesRepresentationconcept has been removed as it was not providing sufficient value
Additionally:
format.handostream.hheader files are now deprecated and their content is (almost always) available through other headers (e.g.,mp-units/quantity.h,mp-units/systems/si.h, ...)- Custom representation type detection has been simplified - no need to specialize
is_scalar,is_vector, andis_tensorcustomization points anymore
Examples and reusability improvements¶
- The
measurement.hheader has been extracted from examples, making it easier to reuse measurement-related utilities in your own code - The
clcpp_responseandconversion_factorexamples have been removed as they were based on outdated patterns
Documentation and tooling¶
This release includes substantial improvements to documentation and development infrastructure:
- Comprehensive API Reference documentation by @JohelEGP
- Highly extended examples section
- Brand new tutorials section (try it out!)
- New cheat sheet for quick reference
- New CONTRIBUTORS.md
- GitHub Codespaces support added (GitPod removed)
- CI matrix generation for better test coverage (thanks @burnpanck)
- GitHub issue templates for bug reports, documentation issues, feature requests, and usage experience
- Pull request templates for better contribution workflow
Community and support¶
If you find mp-units valuable for your projects, please consider:
- Contributing to the project
- Sponsoring development
- Sharing your usage experience
Your support helps ensure continued development and improvement of the library!