A compile-time enabled Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation.

GitHub license Conan CI CMake CI GitHub Workflow Documentation Conan stable Conan testing

mp-units - A Units Library for C++

The mp-units library is the subject of ISO standardization for C++23/26. More on this can be found in ISO C++ paper P1935 and CppCon 2020 talk. We are actively looking for parties interested in field trialing the library.

Documentation

An extensive project documentation including installation instructions and user's guide can be found on mp-units GitHub Pages.

TL;DR

mp-units is a compile-time enabled Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The basic idea and design heavily bases on std::chrono::duration and extends it to work properly with many dimensions.

Here is a small example of possible operations:

#include <units/isq/si/area.h>
#include <units/isq/si/frequency.h>
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>

using namespace units::isq::si::references;

// simple numeric operations
static_assert(10 * km / 2 == 5 * km);

// unit conversions
static_assert(1 * h == 3600 * s);
static_assert(1 * km + 1 * m == 1001 * m);

// dimension conversions
inline constexpr auto kmph = km / h;
static_assert(1 * km / (1 * s) == 1000 * (m / s));
static_assert(2 * kmph * (2 * h) == 4 * km);
static_assert(2 * km / (2 * kmph) == 1 * h);

static_assert(2 * m * (3 * m) == 6 * m2);

static_assert(10 * km / (5 * km) == 2);

static_assert(1000 / (1 * s) == 1 * kHz);

Try it on the Compiler Explorer.

This library requires some C++20 features (concepts, classes as NTTPs, ...). Thanks to them the user gets a powerful but still easy to use interface and all unit conversions and dimensional analysis can be performed without sacrificing on accuracy. Please see the below example for a quick preview of basic library features:

#include <units/format.h>
#include <units/isq/si/international/length.h>
#include <units/isq/si/international/speed.h>
#include <units/isq/si/length.h>
#include <units/isq/si/speed.h>
#include <units/isq/si/time.h>
#include <units/quantity_io.h>
#include <iostream>

using namespace units::isq;

constexpr Speed auto avg_speed(Length auto d, Time auto t)
{
  return d / t;
}

int main()
{
  using namespace units::isq::si::literals;
  using namespace units::isq::si::references;
  using namespace units::aliases::isq::si::international;

  constexpr Speed auto v1 = 110 * (km / h);
  constexpr Speed auto v2 = mi_per_h(70.);
  constexpr Speed auto v3 = avg_speed(220_q_km, 2_q_h);
  constexpr Speed auto v4 = avg_speed(si::length<si::international::mile>(140), si::time<si::hour>(2));
#if UNITS_DOWNCAST_MODE == 0
  constexpr Speed auto v5 = quantity_cast<si::speed<si::metre_per_second>>(v3);
  constexpr Speed auto v6 = quantity_cast<si::dim_speed, si::metre_per_second>(v4);
#else
  constexpr Speed auto v5 = quantity_cast<si::speed<si::metre_per_second>>(v3);
  constexpr Speed auto v6 = quantity_cast<si::metre_per_second>(v4);
#endif
  constexpr Speed auto v7 = quantity_cast<int>(v6);

  std::cout << v1 << '\n';                                  // 110 km/h
  std::cout << v2 << '\n';                                  // 70 mi/h
  std::cout << fmt::format("{}", v3) << '\n';               // 110 km/h
  std::cout << fmt::format("{:*^14}", v4) << '\n';          // ***70 mi/h****
  std::cout << fmt::format("{:%Q in %q}", v5) << '\n';      // 30.5556 in m/s
  std::cout << fmt::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s
  std::cout << fmt::format("{:%Q}", v7) << '\n';            // 31
}

Try it on the Compiler Explorer.

Owner
Mateusz Pusz
Software architect, chief engineer, security champion, and C++ trainer with more than 14 years of experience in designing, writing and maintaining C++ code.
Mateusz Pusz
Comments
  • Poll: Dimensionless quantity (quantity of dimension one)

    Poll: Dimensionless quantity (quantity of dimension one)

    ISO 80000-1:2009(E)

    • Quantity for which all the exponents of the factors corresponding to the base quantities in its quantity dimension are zero.
    • The measurement units and values of quantities of dimension one are numbers, but such quantities convey more information than a number.
    • Some quantities of dimension one are defined as the ratios of two quantities of the same kind.

    Assuming:

    auto q1 = quantity<metre, double>() / quantity<metre, double>();
    auto q2 = quantity<second, double>() / quantity<second, double>();
    

    What the result should be?

    (please vote only once by clicking on the correct bar above)

    • Option 1 (current library design)

      static_assert(std::is_same_v<q1, double>);
      static_assert(std::is_same_v<q2, double>);
      auto q = q1 + q2;
      static_assert(std::is_same_v<q, double>);
      
    • Option 2

      static_assert(std::is_same_v<q1::dimension, dimension<>>);
      static_assert(std::is_same_v<q2::dimension, dimension<>>);
      auto q = q1 + q2;
      static_assert(std::is_same_v<q::dimension, dimension<>>);
      
    • Option 3

      static_assert(std::is_same_v<q1::dimension, dimension<exp<base_dim_length, 0>>>);
      static_assert(std::is_same_v<q2::dimension, dimension<exp<base_dim_time, 0>>>);
      // auto q = q1 + q2;  // does not compile
      

      Please note that in such a case:

      auto q1 = 10mps * 2s;     // length
      auto q2 = q1 / 2m;        // dimensionless<length>
      auto q = 10mps * 2s / 2m; // dimensionless<velocity>
      
  • What are the minimum requirements for a standard units library?

    What are the minimum requirements for a standard units library?

    Q: What are the minimum requirements for a standard units library?

    A: Modelling the S.I. system as closely as possible. The way that is implemented is not really relevant. The S.I is a global standard.

    Problem: Unfortunately the general user has no voice on the c++ committee.

    I worked on a quantity /units library for boost since way back in 2003. I need a library that modelled S.I units.

    My original problem was that I was working on an app to design small wind turbines, using doubles for quantities and getting fed up with checking what units each variable was in, which wasted a lot of time. The library I wrote later solved that issue very nicely. All Si units.

    I see that @mpusz started around the same place except for working on a gliding app. All SI units. https://github.com/mpusz/Condor2Nav I understand the motive exactly.

    Again engineering, not Theoretical Physics. All S.I. Units.

    The library that can (apparently) do all that that Theoretical Phyiscists want is now in Boost but what was created there didn't actually elegantly answer a problem for which there was a general need to be solved - modelling the SI. For the general user, Boost.Units it is way to cumbersome and hard to understand and use. It is time to tell the Physicists to go away and fix their Units library on Boost and to produce their own globally recognised standard, before we spend more of the general users time arguing about their exotic needs.

    That theme of a Physicists making impossible demands about their "essential needs" then disappearing once they are met needs to be remembered. General users need SI units more than they need Natural units. Remember there is no global standard for Natural units. Each physicist enjoys showing why their own version is superior. A library for the general user just doesn't need to support that. And we dont have time to argue about it.

    mp-units should drastically limit its scope so that it caters for the average user who uses SI units, not "smart people", language lawyers or physicists, else there is no way it will or should get in to the C++ standard. What is left will be easier to maintain and certainly more likely to write standard documentation for.

    It would be great if mp_units was a general framework but in practise the minimum is that there needs to be the ability to work with SI quantities, that is simple and intuitive to use. Ultimately the standard is about the interface not the implementation.

    It is time to pare away the std units candidate to the bare minimum requirements

    Just model the SI system, following the docs as closely as possible ( so no angle as dimension for example). Stick to same Rep concept as std::chronos::duration. If anyone complains that the Rep type is no good, tell them to provide a solution fixing it for chrono duration.

  • Use Magnitude instead of ratio in unit implementations

    Use Magnitude instead of ratio in unit implementations

    This PR doesn't aim to convert every last use case. Rather, the goal is to perform the smallest "reasonable" change such that the repo still builds.

    We bundle a few auxiliary changes which could have been separate PRs, but which I included here to reduce the total time-to-land. To reduce reviewers' cognitive load, I've retained them as separate "checkpoint commits". Reviewers can start at the first commit and step through for ease of reading. These auxiliary changes include:

    • Supporting subtraction and spaceship operators in ratio

    • Implementing the "common Magnitude" functionality, which is equivalent to the "largest even divisor" logic we've borrowed from std::chrono in every applicable case, but which is simpler for Magnitudes

    • Loosening the requirements on the Rep of a Magnitude, which is necessary to maintain support for non-is_arithmetic Reps. NOTE: there is some remaining work here to express everything in terms of treat_as_floating_point, which we can either do in this PR, or later, as reviewers prefer.

    The "bulk" of the PR simply performs fairly mechanical migrations from ratio to as_magnitude<ratio>() in various template arguments. Some corner cases and concepts (e.g., UnitRatio) simply melt away, because we don't need them in a Magnitude-based world. The only "asterisk" in this migration is a few test cases in the formatting, where the current implementation produces an equivalent but different representation. I've commented these out, and we can deal with them later.

    Follow-on work includes:

    • Hunting down remaining uses of ratio in template arguments
    • Removing exp
    • Making degrees and revolutions for angle units

    Partially addresses #300.

  • Added ASCII-only output support

    Added ASCII-only output support

    Dear Mr. Mateusz Pusz,

    Much of C++ template programming with C++20 features is still a bit overwhelming for me at this point in time.

    I do not fully understand how the library is written, but I can rely on what I already know about it and my intuition to understand certain parts. I think I get the gist of it.

    I am using this library for SPICE circuit generation using custom templates. I have generator classes that are responsible for rendering the custom templates into SPICE circuit text.

    Initializing my generator: RCCircuitGenerator generator {5ns, 2ms, 5V, 1ms, 10kR, 10nF};

    The formatting string for the line starting with "RLOAD" (see below) looks like this: RLOAD 1 2 {:%Q%q}

    The following SPICE RC circuit text would be generated:

    RC_CIRCUIT
    .TRAN 5ns 2ms
    VSRC 1 0 PULSE 0 5V 0 0 0 1ms
    RLOAD 1 2 10kΩ
    CCHARGE 2 0 10nF
    .END
    

    However, the SPICE simulator library I am using, ngspice, expect ASCII encoding. The simulation crashes when this circuit is passed to the library.

    With the changes I have provided, when the formatting string for the line starting with "RLOAD" looks like this: RLOAD 1 2 {:%Q%a}

    Then the following SPICE RC circuit text would be generated:

    RC_CIRCUIT
    .TRAN 5ns 2ms
    VSRC 1 0 PULSE 0 5V 0 0 0 1ms
    RLOAD 1 2 10kohm
    CCHARGE 2 0 10nF
    .END
    

    And the SPICE simulation then works.

    I hope that my modifications adhere to the style and philosophy of this library as closely as possible.

    Please let me know of any modifications you would like to me perform first if you would like to merge this pull request eventually.

    Thank you very much.

    Ramzi Sabra

  • Raise coherent quantity to fractional power doesnt give correct result.

    Raise coherent quantity to fractional power doesnt give correct result.

    Raise coherent quantity to fractional power doesnt give correct result. Personally I am not so bothered about incoherent quantities but for coherent quantities this can be done better.

    Currently doesnt give the correct answer:

    https://godbolt.org/z/6bgkEK

    For coherent quantities the multiplier of the result should remain as one ,and the exponent should be adjusted ( so for square root divide exponent by 2, and in fact for pow<R>(q) the result exponent is simply multiply the exponent by R ( so for square root multiply by 1/2, for cube 1/3, for pow<3/2> multiply by 3/2. If the exponent is a rational number then it can represent the quantity raised to fractional power correctly and exactly .

  • Hidden Friends issue

    Hidden Friends issue

    Hidden Friends are good because they help with overload resolution and thus are suggested for usage in a modern C++ design.

    However, for quantity it produces issues with implicit conversions. The second assert of the following code compiles fine even though it shouldn't: https://godbolt.org/z/kSrqHT.

    When non-friend version is used it correctly fails to compile: https://godbolt.org/z/32s4cj

    We could provide template parameters for both arguments of a hidden friend implementation: https://godbolt.org/z/2uMsPB. However, in this case we end up with an ambiguous overload set. So we have to constrain it: https://godbolt.org/z/RKaopR. With this, we achieved what we wanted but with a cost of a more complicated signature.

    Should we provide constrained hidden friend version of the operator or non-friend one with all its overload resolution issues?

  • Scalar concept should be renamed

    Scalar concept should be renamed

    The 'units::Scalar' concept definition has nothing to do with any external definition of scalar and is very misleading, https://github.com/mpusz/units/blob/master/src/include/units/quantity.h#L63 should be renamed to RegularNotQuantity or something similar especially since Vector is an allowed rep type https://github.com/mpusz/units/blob/master/example/linear_algebra.cpp#L205

  • Have you considered to add a dimension-point in the design?

    Have you considered to add a dimension-point in the design?

    std::chrono is based on the difference between a point on the time dimension (time_point) and the difference between two such points, a quantity (a duration for the time dimension).

    Do you plan to add such a dimension-point in your library?

  • Will not compile with -fno-exceptions

    Will not compile with -fno-exceptions

    I am working on a bare metal ARM-cortex M project using this units library.

    I just updated to the head of your main branch and changes in src/core/include/units/magnitude.h are causing compilation errors. Specifically the use of throw statements. I have it working now by guarding each throw with the macro

    #if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
        (defined(_MSC_VER) && defined(_CPPUNWIND)) || \
        defined(__EXCEPTIONS)
    

    EDIT: I think this may not be related to -fno-exceptions, since it does compile if I make some small code changes. I am trying to fins the root cause and will update/close issue when I find out more (see my comments below).

    Compiler: arm-none-eabi-11.3.1-rel1 (latest ARM provided toolchain) arch: ARM-v8M-baseline (M23)

  • Support seamless interop between `ratio` and rational `Magnitude`

    Support seamless interop between `ratio` and rational `Magnitude`

    We provide two new functions, numerator(m) and denominator(m), for a Magnitude m. They fulfill the following conditions:

    1. numerator(m) and denominator(m) are always integer Magnitudes.
    2. If m is rational, then m == numerator(m) / denominator(m).

    If m is not rational, then the numerator and denominator are not especially meaningful (there is no uniquely defined "leftover irrational part"). However, we choose a convention that matches how humans would write a mixed number. For example, sqrt(27/16) would have a numerator of 3, denominator of 4, and a "leftover part" of sqrt(3), matching the "human" way of writing this as [(3 * sqrt(3)) / 4]. This has no use yet, but it may later be useful in printing the Magnitude of an anonymous Unit for end users.

    To further reduce friction for the upcoming migration, we provide a conversion from a Magnitude to a ratio. We restrict this operation to rational Magnitudes, and guard this with a static_assert.

  • Add vector space representation for magnitudes

    Add vector space representation for magnitudes

    This is the beginning of the implementation for #300, laying the first foundations. This seems like a good point to check in and align on direction before we get too far along. For general context:

    Things we include here:

    • Concepts for Magnitude, and BasePower (a Magnitude is made up of a canonicalized parameter pack of BasePowers).
    • End user interfaces for making magnitudes:
      • as_magnitude<ratio>(): the main way to make a Magnitude from a rational number. This handles the "heavy lifting" of converting to a prime factorization.
      • base_power<T>{ratio}: we handle irrational bases by introducing a type T, with a static long double member value which must be positive.
    • Enhanced ratio functionality: addition, negation, and implicit construction.

    Work yet undone:

    • A mechanism for applying a magnitude to a given value. (We'll probably want to break out the rational and irrational parts separately, so we can give exact results as often as possible. We'll also want to think carefully about what types we use in intermediate computations---for example, not everyone will want to use long double, and even double can be problematic for some embedded applications.)
    • Actually migrating existing uses to use Magnitude instead of ratio.

    Once we get to something that looks like the right direction, I can flesh out these other items.

  • removing

    removing "extra" typename for GCC breaks clang tooling

    Clang has yet to implement P0634 - Make typename more optional, while GCC has. I presume that is why the following code is in seal/external/mp-units/core/include/units/bits/external/hacks.h

    #if UNITS_COMP_MSVC || UNITS_COMP_CLANG
    
    #define TYPENAME typename
    
    #else
    
    #define TYPENAME
    
    #endif
    

    Even though our main compiler is GCC-11, we do a lot of static analysis with clang tools. This was hurting adoption in my project since any inclusion of our project's voltage type would break static analysis for that file.

    I did notice that this doesn't happen for all types. No issue with meters for example. But voltage is our most common unit and it is a bit more complex.

    I patched our local fork to just have #define TYPENAME typename for all compilers in hacks.h.

    Not sure if this is something you would want to upstream, but maybe having this issue in the list will save someone else a bit of time :).

  • Fix degree symbol formatting

    Fix degree symbol formatting

    According to SI Brochure:

    The numerical value always precedes the unit and a space is always used to separate the unit from the number. Thus the value of the quantity is the product of the number and the unit. The space between the number and the unit is regarded as a multiplication sign (just as a space between units implies multiplication). The only exceptions to this rule are for the unit symbols for degree, minute and second for plane angle, °, ′ and ″, respectively, for which no space is left between the numerical value and the unit symbol.

  • Format general Magnitudes in printed output

    Format general Magnitudes in printed output

    Now that we are using Magnitude, not ratio, we need to decide how a Magnitude should be printed, and then implement that policy. We'll need to take into account, among perhaps other considerations:

    • Factoring out powers of 10
    • Irrational factors, such as (powers of) pi
    • Radical powers of integer factors

    I don't know what the policy should be, but step one is to figure that out, and step two is to implement it.

    (I don't have time right now to work on this, but I thought it important to have an issue to track the pending work and provide a home for discussion.)

  • Reevalute the use of the downcasting facility on units

    Reevalute the use of the downcasting facility on units

    The use of the downcasting facility on units is at odds with

    It is therefore important not to use the unit alone to specify the quantity. This applies not only to technical texts, but also, for example, to measuring instruments (i.e. the instrument read-out needs to indicate both the unit and the quantity measured). -- Extract from https://www.bipm.org/en/publications/si-brochure

    This is quoted at https://github.com/mpusz/units/issues/32#issuecomment-1092376525. I do not think the library should, for example, transform an unit of s⁻¹ to Hz. That conversion should be explicitly specified by the user where it matters, because they carry different information.

    Dropping the use of downcasting facility on units will also remove the source of conflicts between equivalent units that currently want to be downcasted to from the same set of base units.

  • Simplify common magnitude use cases

    Simplify common magnitude use cases

    Most of the magnitudes is the user's code will be the power of 10, only some will be the power of 2, and only a few a power of pi. Other magnitudes probably will nearly not exist. We should optimize for this case so the user does not end up with an error like:

    required for the satisfaction of 'Area<units::quantity<units::unknown_dimension<units::exponent<units::isq::si::dim_length, -1, 1> >, units::scaled_unit<units::magnitude<units::base_power<long int>{2, units::ratio{-3, 1, 0}}, units::base_power<long int>{5, units::ratio{-3, 1, 0}}>(), units::unknown_coherent_unit>, double> >'
    

    Also please note that this error comes from gcc10. gcc11 and gcc12 tend to shorten it making it impossible to know what we are dealing with:

    required for the satisfaction of 'Area<auto [requires units::isq::Area<<placeholder>, >]>' [with auto [requires units::isq::Area<<placeholder>, >] = units::quantity<units::unknown_dimension<units::exponent<units::isq::si::dim_length, -1, 1> >, units::scaled_unit<{}, units::unknown_coherent_unit>, double>]
    

    This impacts user experience a lot.

    See the code in: https://godbolt.org/z/E5b1c54zv.

Windows user-land hooks manipulation tool.
Windows user-land hooks manipulation tool.

MineSweeper Windows user-land hooks manipulation tool. Highlights Supports any x64/x86 Windows DLL (actually, any x64/x86 Windows PE for that matter)

Aug 10, 2022
cavi is an open-source library that aims to provide performant utilities for closed hierarchies (i.e. all class types of the hierarchy are known at compile time).

cavi cavi is an open-source library that aims to provide performant utilities for closed hierarchies (i.e. all class types of the hierarchy are known

Mar 9, 2022
A header only C++ library that provides type safety and user defined literals for physical units

SI - Type safety for physical units A header only c++ library that provides type safety and user defined literals for handling pyhsical values defined

Sep 20, 2022
Compile and execute C "scripts" in one go!
Compile and execute C

c "There isn't much that's special about C. That's one of the reasons why it's fast." I love C for its raw speed (although it does have its drawbacks)

Sep 22, 2022
LibOS is a modern C++17 library that makes OS-specific features cross-platform.

LibOS is a modern C++17 library that makes OS-specific features cross-platform. Ever tried to get Windows version after Windows 8? Or to send ke

Sep 13, 2022
The lightweight and modern Map SDK for Android and iOS
The lightweight and modern Map SDK for Android and iOS

Open Mobile Maps The lightweight and modern Map SDK for Android (6.0+) and iOS (10+) openmobilemaps.io Getting started Readme Android Readme iOS Featu

Sep 20, 2022
An attempt to restore and adapt to modern Win10 version the Rootkit Arsenal original code samples

rootkit-arsenal-guacamole An attempt to restore and adapt to modern Win10 version the Rootkit Arsenal original code samples All projects have been por

Jul 25, 2022
The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.

The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.

Sep 15, 2022
The most powerful and customizable binary pattern scanner written on modern C++

Sig The most powerful and customizable binary pattern scanner written on modern C++ ✔ Capabilities: Support for all common pattern formats: Pattern +

Sep 13, 2022
A Template Engine for Modern C++

Inja is a template engine for modern C++, loosely inspired by jinja for python. It has an easy and yet powerful template syntax with all variables, lo

Sep 18, 2022
Mustache text templates for modern C++

About Mustache implementation for modern C++ (requires C++11) Header only Zero dependencies Templated string type for compatibility with any STL-like

Sep 13, 2022
Pretty Printer for Modern C++
Pretty Printer for Modern C++

Highlights Single header file Requires C++17 MIT License Quick Start Simply include pprint.hpp and you're good to go. #include <pprint.hpp> To start p

Sep 21, 2022
The libxo library allows an application to generate text, XML, JSON, and HTML output using a common set of function calls. The application decides at run time which output style should be produced.

libxo libxo - A Library for Generating Text, XML, JSON, and HTML Output The libxo library allows an application to generate text, XML, JSON, and HTML

Sep 20, 2022
This library support run-time type casting faster than dynamic_cast ( similar to unreal engine's CastTo )
This library support run-time type casting faster than dynamic_cast ( similar to unreal engine's CastTo )

Fast Runtime Type Casting This library give you C++ Fast Runtime Type Casting faster than dynamic_cast ( similar to Unreal Engine's CastTo, IsChildOf

Jun 11, 2022
Sqrt OS is a simulation of an OS scheduler and memory manager using different scheduling algorithms including Highest Priority First (non-preemptive), Shortest Remaining Time Next, and Round Robin.
Sqrt OS is a simulation of an OS scheduler and memory manager using different scheduling algorithms including Highest Priority First (non-preemptive), Shortest Remaining Time Next, and Round Robin.

A CPU scheduler determines an order for the execution of its scheduled processes; it decides which process will run according to a certain data structure that keeps track of the processes in the system and their status. A process, upon creation, has one of the three states: Running, Ready, Blocked (doing I/O, using other resources than CPU or waiting on unavailable resource).

Apr 15, 2022
Advanced, modular, coupled geomorpohology simulator for real-time procedural terrain generation.
Advanced, modular, coupled geomorpohology simulator for real-time procedural terrain generation.

SoilMachine Advanced, modular, coupled geomorpohology simulator for real-time procedural terrain generation in C++. Visualization with TinyEngine. Art

Sep 11, 2022
provide SFML Time utilities in pure C++20, no dependencies

SFML-Time-utilities-without-SFML provide SFML Time utilities in pure C++20, no dependencies Example int main() { Clock clock; Sleep(1000);

Apr 28, 2022
DateTime Class to Express Time

Time is an eternal topic. How to express time is very important. The C + + class represents the date and time, which is very easy to use.

Dec 7, 2021