Skip to content

hello_units

This is a really simple example showcasing the features of the mp-units library.

First, we either import the mp_units module or include the headers for:

  • an International System of Quantities (ISQ),
  • an International System of units (SI),
  • units derived from the International Yard and Pound,
  • text formatting and stream output support.
hello_units.cpp
#include <mp-units/compat_macros.h>
#include <mp-units/ext/format.h>
#ifdef MP_UNITS_IMPORT_STD
import std;
#else
#include <iomanip>
#include <iostream>
#endif
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/format.h>
#include <mp-units/ostream.h>
#include <mp-units/systems/international.h>
#include <mp-units/systems/isq.h>
#include <mp-units/systems/si.h>
#endif

Also, to shorten the definitions, we "import" all the symbols from the mp_units namespace.

hello_units.cpp
using namespace mp_units;

Next, we define a simple function that calculates the average speed based on the provided arguments of length and time:

hello_units.cpp
constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto t)
{
  return d / t;
}

The above function template takes any quantities implicitly convertible to isq::length and isq::time, respectively. Those quantities can use any compatible unit and a representation type. The function returns a result of a straightforward equation and ensures that its quantity type is implicitly convertible to isq::speed.

Tip

Besides verifying the type returned from the function, constraining a generic return type is beneficial for users of such a function as it provides more information of what to expect from a function than just using auto.

hello_units.cpp
int main()
{
  using namespace mp_units::si::unit_symbols;
  using namespace mp_units::international::unit_symbols;

The above lines explicitly opt into using unit symbols from two systems of units. As this introduces a lot of short identifiers into the current scope, it is not done implicitly while including a header file.

hello_units.cpp
  constexpr quantity v1 = 110 * km / h;
  constexpr quantity v2 = 70 * mph;
  constexpr quantity v3 = avg_speed(220. * km, 2 * h);
  constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * isq::duration[h]);
  constexpr quantity v5 = v3.in(m / s);
  constexpr quantity v6 = value_cast<m / s>(v4);
  constexpr quantity v7 = value_cast<int>(v6);
  • Lines 27 & 28 create a quantity of kind isq::length / isq::time with the numbers and units provided. Such quantities can be converted or assigned to any other quantity with a matching kind.
  • Line 29 calls our function template with quantities of kind isq::length and isq::time and number and units provided.
  • Line 30 explicitly provides quantity types of the quantities passed to a function template. This time, those will not be quantity kinds anymore and will have more restrictive conversion rules.
  • Line 31 changes the unit of a quantity v3 to m / s in a value-preserving way (floating-point representations are considered to be value-preserving).
  • Line 32 does a similar operation, but this time, it would also succeed for value-truncating cases (if that was the case).
  • Line 33 does a value-truncating conversion of changing the underlying representation type from double to int.
hello_units.cpp
  std::cout << v1 << '\n';                                           // 110 km/h
  std::cout << std::setw(10) << std::setfill('*') << v2 << '\n';     // ***70 mi/h
  std::cout << MP_UNITS_STD_FMT::format("{:*^10}\n", v3);            // *110 km/h*
  std::cout << MP_UNITS_STD_FMT::format("{:%N in %U of %D}\n", v4);  // 70 in mi/h of LT⁻¹
  std::cout << MP_UNITS_STD_FMT::format("{::N[.2f]}\n", v5);         // 30.56 m/s
  std::cout << MP_UNITS_STD_FMT::format("{::N[.2f]U[dn]}\n", v6);    // 31.29 m⋅s⁻¹
  std::cout << MP_UNITS_STD_FMT::format("{:%N}\n", v7);              // 31
}

The above presents various ways to print a quantity. Both stream insertion operations and std::format facilities are supported.

Tip

MP_UNITS_STD_FMT is used for compatibility reasons. If a specific compiler does not support std::format or a user prefers to use the {fmt} library, this macro will resolve to fmt namespace. Otherwise, the std namespace will be used.

More about it can be found in the Wide Compatibility chapter.