Files
mp-units/2.0/search/search_index.json
T

1 line
257 KiB
JSON

{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to mp-units!","text":"<p>mp-units is a compile-time enabled feature-rich Modern C++ header-only library that provides compile-time dimensional analysis and unit/quantity manipulation. Its key strengths include safety, performance, and developer experience.</p> <p>The library source code is hosted on GitHub with a permissive MIT license.</p> <p>Help needed!</p> <p>The mp-units library might be the subject of ISO standardization for C++29. More on this can be found in the ISO C++ proposal P1935. We are actively looking for parties interested in field-trialing the library.</p> Supported compilers <p>This library tries to provide the best user experience possible with the C++ language. To achieve that, it extensively uses C++20 features and the explicit object parameter from C++23.</p> <p>Even though the library benefits from C++23 (if available), C++20 is enough to compile and use all of the library's functionality. C++23 features are hidden behind a preprocessor macro providing a backward-compatible way to use it.</p> <p>As of today, the library compiles fine on the following compilers (or newer):</p> <ul> <li>gcc-11<ul> <li>please note that we observed some ICEs on gcc-11</li> <li>no problems with gcc-12.2+</li> </ul> </li> <li>clang-16</li> </ul>"},{"location":"appendix/glossary/","title":"Glossary","text":""},{"location":"appendix/glossary/#iso-definitions","title":"ISO definitions","text":"<p>Note</p> <p>The ISO terms provided below are only a few of many defined in the ISO/IEC Guide 99.</p> <code>quantity</code> <ul> <li>Property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed by means of a number and a reference.</li> <li>A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.</li> <li>A quantity as defined here is a scalar. However, a vector or a tensor, the components of which are quantities, is also considered to be a quantity.</li> <li>The concept \u2019quantity\u2019 may be generically divided into, e.g. \u2018physical quantity\u2019, \u2018chemical quantity\u2019, and \u2018biological quantity\u2019, or \u2018base quantity\u2019 and \u2018derived quantity\u2019.</li> <li>Examples of quantities are: length, radius, wavelength, energy, electric charge, etc.</li> </ul> <code>kind of quantity, kind</code> <ul> <li>Aspect common to mutually comparable quantities.</li> <li>The division of the concept \u2018quantity\u2019 into several kinds is to some extent arbitrary, for example:<ul> <li>the quantities diameter, circumference, and wavelength are generally considered to be quantities of the same kind, namely, of the kind of quantity called length,</li> <li>the quantities heat, kinetic energy, and potential energy are generally considered to be quantities of the same kind, namely of the kind of quantity called energy.</li> </ul> </li> <li>Quantities of the same kind within a given system of quantities have the same quantity dimension. However, quantities of the same dimension are not necessarily of the same kind.<ul> <li>For example, the quantities moment of force and energy are, by convention, not regarded as being of the same kind, although they have the same dimension. Similarly for heat capacity and entropy, as well as for number of entities, relative permeability, and mass fraction.</li> </ul> </li> </ul> <code>system of quantities</code> <ul> <li>Set of quantities together with a set of non-contradictory equations relating those quantities.</li> <li>Examples of systems of quantities are: the International System of Quantities, the Imperial System, etc.</li> </ul> <code>base quantity</code> <ul> <li>Quantity in a conventionally chosen subset of a given system of quantities, where no quantity in the subset can be expressed in terms of the others.</li> <li>Base quantities are referred to as being mutually independent since a base quantity cannot be expressed as a product of powers of the other base quantities.</li> <li>\u2018Number of entities\u2019 can be regarded as a base quantity in any system of quantities.</li> </ul> <code>derived quantity</code> <ul> <li>Quantity, in a system of quantities, defined in terms of the base quantities of that system.</li> </ul> <code>International System of Quantities, ISQ</code> <ul> <li>System of quantities based on the seven base quantities: length, mass, time, electric current, thermodynamic temperature, amount of substance, and luminous intensity.</li> <li>This system of quantities is published in the ISO 80000 and IEC 80000 series Quantities and units.</li> <li>The International System of Units (SI) is based on the ISQ.</li> </ul> <code>quantity dimension, dimension of a quantity, dimension</code> <ul> <li>Expression of the dependence of a quantity on the base quantities of a system of quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factor.<ul> <li>i.e. in the ISQ, the quantity dimension of force is denoted by \\(\\textsf{dim }F = \\mathsf{LMT}^{\u20132}\\).</li> </ul> </li> <li>A power of a factor is the factor raised to an exponent. Each factor is the dimension of a base quantity.</li> <li>In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.</li> <li>In a given system of quantities:<ul> <li>quantities of the same kind have the same quantity dimension,</li> <li>quantities of different quantity dimensions are always of different kinds,</li> <li>quantities having the same quantity dimension are not necessarily of the same kind.</li> </ul> </li> <li> <p>Symbols representing the dimensions of the base quantities in the ISQ are:</p> Base quantity Symbol for dimension length \\(\\mathsf{L}\\) mass \\(\\mathsf{M}\\) time \\(\\mathsf{T}\\) electric current \\(\\mathsf{I}\\) thermodynamic temperature \\(\\mathsf{\u0398}\\) amount of substance \\(\\mathsf{N}\\) luminous intensity \\(\\mathsf{J}\\) <p>Thus, the dimension of a quantity \\(Q\\) is denoted by \\(\\textsf{dim }Q = \\mathsf{L}^\u03b1\\mathsf{M}^\u03b2\\mathsf{T}^\u03b3\\mathsf{I}^\u03b4\\mathsf{\u0398}^\u03b5\\mathsf{N}^\u03b6\\mathsf{J}^\u03b7\\) where the exponents, named dimensional exponents, are positive, negative, or zero.</p> </li> </ul> <code>quantity of dimension one, dimensionless quantity</code> <ul> <li>quantity for which all the exponents of the factors corresponding to the base quantities in its quantity dimension are zero.</li> <li>The term \u201cdimensionless quantity\u201d is commonly used and is kept here for historical reasons. It stems from the fact that all exponents are zero in the symbolic representation of the dimension for such quantities. The term \u201cquantity of dimension one\u201d reflects the convention in which the symbolic representation of the dimension for such quantities is the symbol \\(1\\).</li> <li>The measurement units and values of quantities of dimension one are numbers, but such quantities convey more information than a number.</li> <li>Some quantities of dimension one are defined as the ratios of two quantities of the same kind.</li> <li>Numbers of entities are quantities of dimension one.</li> </ul> <code>measurement unit, unit of measurement, unit</code> <ul> <li>Real scalar quantity, defined and adopted by convention, with which any other quantity of the same kind can be compared to express the ratio of the two quantities as a number.</li> <li>Measurement units are designated by conventionally assigned names and symbols.</li> <li>Measurement units of quantities of the same quantity dimension may be designated by the same name and symbol even when the quantities are not of the same kind.<ul> <li>For example, joule per kelvin and J/K are respectively the name and symbol of both a measurement unit of heat capacity and a measurement unit of entropy, which are generally not considered to be quantities of the same kind. However, in some cases special measurement unit names are restricted to be used with quantities of specific kind only. For example, the measurement unit \u2018second to the power minus one\u2019 (\\(\\mathsf{1/s}\\)) is called hertz (\\(\\mathsf{Hz}\\)) when used for frequencies and becquerel (\\(\\mathsf{Bq}\\)) when used for activities of radionuclides. As another example, the joule (\\(\\mathsf{J}\\)) is used as a unit of energy, but never as a unit of moment of force, i.e. the newton metre (\\(\\mathsf{N\u00b7m}\\)).</li> </ul> </li> <li>Measurement units of quantities of dimension one are numbers. In some cases, these measurement units are given special names, e.g. radian, steradian, and decibel, or are expressed by quotients such as millimole per mole equal to \\(10^{\u22123}\\) and microgram per kilogram equal to \\(10^{\u22129}\\).</li> </ul> <code>base unit</code> <ul> <li>Measurement unit that is adopted by convention for a base quantity.</li> <li>In each coherent system of units, there is only one base unit for each base quantity.<ul> <li>i.e. in the SI, the metre is the base unit of length. In the CGS systems, the centimetre is the base unit of length.</li> </ul> </li> <li>A base unit may also serve for a derived quantity of the same quantity dimension.</li> <li>For number of entities, the number one, symbol \\(1\\), can be regarded as a base unit in any system of units.</li> </ul> <code>derived unit</code> <ul> <li>Measurement unit for a derived quantity.</li> <li>For example, the metre per second, symbol m/s, and the centimetre per second, symbol cm/s, are derived units of speed in the SI. The kilometre per hour, symbol km/h, is a measurement unit of speed outside the SI but accepted for use with the SI. The knot, equal to one nautical mile per hour, is a measurement unit of speed outside the SI.</li> </ul> <code>coherent derived unit</code> <ul> <li>Derived unit that, for a given system of quantities and for a chosen set of base units, is a product of powers of base units with no other proportionality factor than one.</li> <li>A power of a base unit is the base unit raised to an exponent.</li> <li>Coherence can be determined only with respect to a particular system of quantities and a given set of base units.<ul> <li>For example, if the metre, the second, and the mole are base units, the metre per second is the coherent derived unit of velocity when velocity is defined by the quantity equation \\(v = \\mathsf{d}r/\\mathsf{d}t\\), and the mole per cubic metre is the coherent derived unit of amount-of-substance concentration when amount-of-substance concentration is defined by the quantity equation \\(c = n/V\\). The kilometre per hour and the knot, given as examples of derived units, are not coherent derived units in such a system of quantities.</li> </ul> </li> <li>A derived unit can be coherent with respect to one system of quantities but not to another.<ul> <li>For example, the centimetre per second is the coherent derived unit of speed in a CGS system of units but is not a coherent derived unit in the SI.</li> </ul> </li> <li>The coherent derived unit for every derived quantity of dimension one in a given system of units is the number one, symbol \\(1\\). The name and symbol of the measurement unit one are generally not indicated.</li> </ul> <code>system of units</code> <ul> <li>Set of base units and derived units, together with their multiples and submultiples, defined in accordance with given rules, for a given system of quantities.</li> </ul> <code>coherent system of units</code> <ul> <li>System of units, based on a given system of quantities, in which the measurement unit for each derived quantity is a coherent derived unit.</li> <li>A system of units can be coherent only with respect to a system of quantities and the adopted base units.</li> <li>For a coherent system of units, numerical value equations have the same form, including numerical factors, as the corresponding quantity equations.</li> </ul> <code>off-system measurement unit, off-system unit</code> <ul> <li>Measurement unit that does not belong to a given system of units.</li> <li>For example, the electronvolt (about \\(1.602\\;18 \u00d7 10^{\u201319}\\;\\mathsf{J}\\)) is an off-system measurement unit of energy with respect to the SI. Day, hour, minute are off-system measurement units of time with respect to the SI.</li> </ul> <code>International System of Units, SI</code> <ul> <li>System of units, based on the International System of Quantities, their names and symbols, including a series of prefixes and their names and symbols, together with rules for their use, adopted by the General Conference on Weights and Measures (CGPM).</li> </ul> <code>quantity value, value of a quantity, value</code> <ul> <li>Number and reference together expressing magnitude of a quantity.<ul> <li>For example, length of a given rod: \\(5.34\\;\\mathsf{m}\\) or \\(534\\;\\mathsf{cm}\\).</li> </ul> </li> <li>The number can be complex.</li> <li>A quantity value can be presented in more than one way.</li> <li>In the case of vector or tensor quantities, each component has a quantity value.<ul> <li>For example, force acting on a given particle, e.g. in Cartesian components \\((F_x; F_y; F_z) = (\u221231.5; 43.2; 17.0)\\;\\mathsf{N}\\).</li> </ul> </li> </ul> <code>numerical quantity value, numerical value of a quantity, numerical value</code> <ul> <li>Number in the expression of a quantity value, other than any number serving as the reference<ul> <li>For example, in an amount-of-substance fraction equal to \\(3\\;\\mathsf{mmol/mol}\\), the numerical quantity value is \\(3\\) and the unit is \\(\\mathsf{mmol/mol}\\). The unit \\(\\mathsf{mmol/mol}\\) is numerically equal to \\(0.001\\), but this number \\(0.001\\) is not part of the numerical quantity value, which remains \\(3\\).</li> </ul> </li> </ul> <code>quantity equation</code> <ul> <li>Mathematical relation between quantities in a given system of quantities, independent of measure\u00adment units.</li> <li>For example, \\(T = (1/2) mv^2\\) where \\(T\\) is the kinetic energy and \\(v\\) the speed of a specified particle of mass \\(m\\).</li> </ul> <code>unit equation</code> <ul> <li>Mathematical relation between base units, coher\u00adent derived units or other measurement units.</li> <li>For example, \\(\\mathsf{J} := \\mathsf{kg}\\:\\mathsf{m}^2/\\mathsf{s}^2\\), where, \\(\\mathsf{J}\\), \\(\\mathsf{kg}\\), \\(\\mathsf{m}\\), and \\(\\mathsf{s}\\) are the symbols for the joule, kilogram, metre, and second, respectively. (The symbol \\(:=\\) denotes \u201cis by definition equal to\u201d as given in the ISO 80000 and IEC 80000 series.). \\(1\\;\\mathsf{km/h} = (1/3.6)\\;\\mathsf{m/s}\\).</li> </ul> <code>numerical value equation, numerical quantity value equation</code> <ul> <li>Mathematical relation between numerical quantity values, based on a given quantity equation and specified measurement units.</li> <li>For example, in the quantity equation for kinetic energy of a particle, \\(T = (1/2) mv^2\\), if \\(m = 2\\;\\mathsf{kg}\\) and \\(v = 3\\;\\mathsf{m/s}\\), then \\({T} = (1/2)\\:\u00d7\\:2\\:\u00d7\\:3^2\\) is a numerical value equation giving the numerical value \\(9\\) of \\(T\\) in joules.</li> </ul>"},{"location":"appendix/glossary/#other-definitions","title":"Other definitions","text":"<p>Info</p> <p>The below terms extend the official ISO glossary and are commonly referred to by the mp-units library.</p> <code>base dimension</code> <ul> <li>A dimension of a base quantity.</li> </ul> <code>derived dimension</code> <ul> <li>A dimension of a derived quantity.</li> <li>Implemented as an expression template being the result of the dimension equation on base dimensions.</li> </ul> <code>dimension equation</code> <ul> <li>Mathematical relation between dimensions in a given system of quantities, independent of measure\u00adment units.</li> </ul> <code>quantity kind hierarchy, quantity hierarchy</code> <ul> <li>Quantities of the same kind form a hierarchy that determines their:<ul> <li>convertibility (i.e. every width is a length, but width should not be convertible to height)</li> <li>common quantity type (i.e. width + height -&gt; length)</li> </ul> </li> </ul> <code>quantity character, character of a quantity, character</code> <ul> <li>Scalars, vectors and tensors are mathematical objects that can be used to denote certain physical quantities and their values. They are as such independent of the particular choice of a coordinate system, whereas each scalar component of a vector or a tensor and each component vector and component tensor depend on that choice.</li> <li>A vector is a tensor of the first order and a scalar is a tensor of order zero.</li> <li>For vectors and tensors, the components are quantities that can be expressed as a product of a number and a unit.</li> <li>Vectors and tensors can also be expressed as a numerical value vector or tensor, respectively, multiplied by a unit.</li> <li>Quantities of different characters support different set of operations.<ul> <li>For example, a quantity can be multiplied by another one only if any of them has scalar character. Vectors and tensors can't be multiplied or divided, but they support additional operations like dot and cross products, which are not available for scalars.</li> </ul> </li> <li>The term \u2019character\u2019 was borrowed from the below quote:</li> </ul> <p>ISO 80000-1_2009</p> <p>In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.</p> <code>quantity specification, quantity_spec</code> <ul> <li>An entity storing all the information about a specific quantity:<ul> <li>location in a quantity hierarchy</li> <li>quantity equation</li> <li>dimension of a quantity</li> <li>quantity kind</li> <li>quantity character</li> <li>additional constraints (i.e. non-negative)</li> </ul> </li> <li>Dimension of a quantity is not enough to specify all the properties of a quantity.</li> </ul> <code>unit with an associated quantity, associated unit</code> <ul> <li>Unit that is used to measure quantities of a specific kind in a given system of units.</li> </ul> <code>quantity reference, reference</code> <ul> <li>According to its definition, quantity can be expressed by means of a number and a reference</li> <li>In the mp-units library, a reference describes all the required meta-information associated with a specific quantity (quantity specification and unit).</li> </ul> <code>absolute quantity point origin</code>, <code>absolute point origin</code> <ul> <li>An explicit point on an axis of values of a specific quantity type that serves as an absolute reference point for all quantity points which definitions are (explicitly or implicitly) based on it.</li> <li>For example, mean sea level is commonly used as an absolute reference point to measure altitudes.</li> </ul> <code>relative quantity point origin</code>, <code>relative point origin</code> <ul> <li>An explicit, known at compile-time, point on an axis of values of a specific quantity type serving as a reference for other quantities.</li> <li>For example, an ice point is a quantity point with a value of \\(273.15\\;\\mathsf{K}\\) that is used as the zero point of a degree Celsius scale.</li> </ul> <code>quantity point origin</code>, <code>point origin</code> <ul> <li>Either an absolute point origin or a relative point origin.</li> </ul> <code>quantity point</code>, <code>absolute quantity</code> <ul> <li>An absolute quantity with respect to an origin.</li> <li>For example, timestamp (as opposed to duration), altitude (as opposed to height), absolute temperature (as opposed to temperature difference).</li> </ul>"},{"location":"appendix/references/","title":"References","text":"<code>ISO80000</code> <p>ISO 80000-1:2009(E) \"Quantities and units \u2014 Part 1: General\", International Organization for Standardization.</p> <code>SIBrochure</code> <p>The International System of Units (SI), International Bureau of Weights and Measures (20 May 2019), ISBN 978-92-822-2272-0.</p> <code>Quincey</code> \"Angles in the SI: a detailed proposal for solving the problem, Quincey, Paul (1 October 2021)."},{"location":"appendix/release_notes/","title":"Release Notes","text":""},{"location":"appendix/release_notes/#mp-units","title":"mp-units","text":""},{"location":"appendix/release_notes/#2.1.0","title":"2.1.0 WIP","text":""},{"location":"appendix/release_notes/#2.0.0","title":"2.0.0 September 24, 2023","text":"<ul> <li><code>units</code> namespace renamed to <code>mp_units</code> (#317)</li> <li>header files in the <code>&lt;mp-units/...&gt;</code> rather then in <code>&lt;units/...&gt;</code> (#317)</li> <li>the downcasting facility is removed (#383, #211, #32)</li> <li>unified and simplified quantity creation (#274)</li> <li>determining the best way to create a quantity (#413)</li> <li>V2 <code>quantity_point</code> (#414)</li> <li>introduction of <code>quantity_spec</code> to store not only <code>dimension</code> but also additional information about quantities (#405)</li> <li><code>quantity</code> now takes <code>reference</code> object, which aggregates <code>quantity_spec</code> and a <code>unit</code> and a <code>representation</code> type</li> <li>units, prefixes, dimensions, quantity specifications, and references are passed as NTTPs to templates and provide arithmetic operations and comparison</li> <li>expression templates consistently used in all derived types to increase the readability (#351, #166)</li> <li>derived dimensions are now factors of only base dimensions (#281)</li> <li>convertibility of derived quantities (#427)</li> <li>dimensions, quantity specifications, units, and references are now composable, significantly reducing the number of definitions and resulting types</li> <li>heavily simplified unit systems definitions (no need to define unnamed derived units, systems-specific dimensions, aliases for quantities, concepts, UDLs, ... anymore)</li> <li>improved definition of all systems</li> <li>support for ISO 80000 Part 3-6 quantities</li> <li>faster than lightspeed constants (#169)</li> <li>extensions to quantity formatting with <code>fmt</code></li> <li><code>quantity_kind</code> removed</li> <li>improved casting of unit with <code>.in(Unit)</code>, <code>.force_in(Unit)</code> for <code>quantity</code> and <code>quantity_spec</code></li> <li>numerical value accessor safety improved with <code>.numerical_value_in(Unit)</code> and <code>.force_numerical_value_in(Unit)</code></li> <li><code>quantity</code> can no longer be constructed with a raw value (#434)</li> <li>Implicit construction of quantities from a value (#410)</li> <li><code>quantity_point</code> can no longer be constructed with just a <code>quantity</code> and an explicit <code>PointOrigin</code> is always needed</li> <li><code>ceil</code> and <code>floor</code> are dangerous (#432)</li> <li>quecto, ronto, ronna, quetta new SI prefixes support</li> <li>comparison against zero added (#487)</li> <li>documentation rewritten from scratch</li> <li>many smaller changes not possible to address with the previous design (#205, #210, #134)</li> </ul>"},{"location":"appendix/release_notes/#0.8.0","title":"0.8.0 June 14, 2023","text":"<ul> <li>(!) refactor: <code>common_quantity</code>, <code>common_quantity_for</code>, <code>common_quantity_point</code>, <code>common_quantity_kind</code>, and <code>common_quantity_point_kind</code> removed</li> <li>(!) refactor: <code>named_derived_unit</code> removed as it was not used</li> <li>(!) refactor: <code>derived_unit</code> renamed to <code>derived_scaled_unit</code></li> <li>(!) refactor: <code>unit</code> renamed to <code>derived_unit</code></li> <li>(!) refactor: <code>U::is_named</code> removed from the unit types and replaced with <code>NamedUnit</code> concept</li> <li>(!) refactor: <code>PrefixFamily</code> support removed</li> <li>(!) refactor: <code>mi(naut)</code> renamed to <code>nmi</code></li> <li>(!) refactor: <code>knot</code> unit helper renamed to <code>kn</code> in FPS</li> <li>(!) refactor: <code>knot</code> text symbol changed from <code>\"knot\"</code> to <code>\"kn\"</code></li> <li>refactor: <code>quantity</code> <code>op+()</code> and <code>op-()</code> reimplemented in terms of <code>reference</code> rather then <code>quantity</code> types</li> <li>refactor(example): <code>glide_computer</code> now use dimensionless quantities with <code>ranged_representation</code> as <code>rep</code></li> <li>feat: HEP system support added (thanks @RalphSteinhagen)</li> <li>feat: <code>floor()</code>, <code>ceil()</code>, and <code>round()</code> support added (thanks @hofbi)</li> <li>feat: <code>std::format</code> support for compliant compilers added</li> <li>feat: conversion helpers from <code>mp-units</code> to <code>std::chrono</code> types added</li> <li>feat: math functions can now be safely used with user-defined types</li> <li>feat: conversion from <code>quantity_point</code> to <code>std::chrono::time_point</code> added</li> <li>feat: <code>nautical_mile_per_hour</code> and <code>knot</code> added to <code>si::international</code> system</li> <li>(!) fix: add <code>quantity_point::origin</code>, like <code>std::chrono::time_point::clock</code></li> <li>fix: enable any prefixes for most of the named units (beside those that use prefixes already)</li> <li>fix: <code>hectare</code> definition fixed to be a prefixed version of <code>are</code> + other units</li> <li>fix: account for different dimensions in <code>quantity_point_cast</code>'s constraint</li> <li>fix: output stream operator now properly handles state</li> <li>fix: <code>fmt</code> algorithms were overconstrained with <code>forward_iterator</code></li> <li>fix: CTAD for aliases fixed</li> <li>fix: <code>derived_ratio</code> calculation</li> <li>fix: <code>fill_t</code> assignment operator fixed</li> <li>fix: improve downcast mode off</li> <li>fix: <code>radioactivity</code> header compilation fixed</li> <li>fix: <code>si::hep::dim_momentum</code> duplicated definition fixed</li> <li>fix: <code>fps</code> can now coexist with <code>international</code> system</li> <li>fix: public headers fixed to be standalone</li> <li>test: standalone public headers tests added</li> <li>(!) build: CMake generator in Conan is no longer obtained from an environment variable</li> <li>(!) build: Required Conan version bumped to 1.48</li> <li>(!) build: Conan 1.48 does not set <code>CMAKE_BUILD_TYPE</code> in the <code>conan_toolchain.cmake</code> anymore</li> <li>build: AppleClang 13 support added (thanks @fdischner)</li> <li>build: most of the <code>conanfile.py</code> refactored to be Conan 2.0 ready</li> <li>build: <code>validate()</code> replaced with <code>configure()</code> to raise errors during <code>conan install</code> in Conan 1.X</li> <li>build: minimum Conan version changed to 1.40</li> <li>build: <code>linear-algebra</code> Conan repo is no needed anymore</li> <li>build: Gitpod support added</li> <li>build: clang-format-15 support added</li> <li>build: export config to local build (#322)</li> <li>build: fix export name of <code>mp-units-system</code></li> <li>build: fmt updated to 8.0.1</li> <li>build: gsl-lite updated to 0.40.0</li> <li>build: catch2 updated to 2.13.9</li> <li>build: doxygen updated to 1.9.4</li> <li>build: linear_algebra/0.7.0 switched to wg21-linear_algebra/0.7.2</li> <li>ci: VS2022, gcc-11, clang-13, clang-14, and AppleClang 13 support added</li> <li>ci: pre-commit support added (thanks @hofbi)</li> <li>docs: Project documentation updated</li> <li>docs: <code>CITATION.cff</code> file added</li> <li>docs: <code>CONTRIBUTING.md</code> updated</li> </ul>"},{"location":"appendix/release_notes/#0.7.0","title":"0.7.0 May 11, 2021","text":"<ul> <li>(!) refactor: <code>ScalableNumber</code> renamed to <code>Representation</code></li> <li>(!) refactor: output stream operators moved to the <code>units/quantity_io.h</code> header file</li> <li>(!) refactor: Refactored the library file tree</li> <li>(!) refactor: <code>quantity::count()</code> renamed to <code>quantity::number()</code></li> <li>(!) refactor: <code>data</code> system renamed to <code>isq::iec80000</code> (quantity names renamed too)</li> <li>(!) refactor: <code>*deduced_unit</code> renamed to <code>*derived_unit</code></li> <li>(!) refactor: got rid of a <code>noble_derived_unit</code></li> <li>refactor: quantity (kind) point updated to reflect latest changes to <code>quantity</code></li> <li>refactor: basic concepts, <code>quantity</code> and <code>quantity_cast</code> refactored</li> <li>refactor: <code>abs()</code> definition refactored to be more explicit about the return type</li> <li>feat: quantity (point) kind support added (thanks @johelegp)</li> <li>feat: quantity references support added (thanks @johelegp)</li> <li>feat: quantity aliases support addded</li> <li>feat: interoperability with <code>std::chrono::duration</code> and other units libraries</li> <li>feat: CTAD for dimensionless quantity added</li> <li>feat: <code>modulation_rate</code> support added (thanks @go2sh)</li> <li>feat: SI prefixes for <code>isq::iec80000</code> support added (thanks @go2sh)</li> <li>feat: a possibility to disable quantity UDLs support with <code>UNITS_NO_LITERALS</code> preprocessor define</li> <li>feat: a support to define ISQ derived dimensions in terms of different number or order of components</li> <li>perf: preconditions check do not influence the runtime performance of a Release build</li> <li>perf: <code>quantity_cast()</code> generates less assembly instructions</li> <li>perf: temporary string creation removed from <code>quantity::op&lt;&lt;()</code></li> <li>perf: value initialization for quantity value removed (left with a default initialization)</li> <li>perf: limited the <code>equivalent</code> trait usage</li> <li>perf: limited the C++ Standard Library headers usage</li> <li>perf: rvalue references support added for constructors and getters</li> <li>(!) fix: <code>exp()</code> has sense only for dimensionless quantities</li> <li>(!) fix: <code>dim_torque</code> now properly divides by an angle (instead of multiply) + default unit name change</li> <li>fix: quantity's operators fixed to behave like the underlying types do</li> <li>fix: <code>quantity_cast()</code> fixed to work correctly with representation types not convertible from <code>std::intmax_t</code></li> <li>fix: ambiguous case for empty type list resolved</li> <li>fix: downcasting facility for non-default-constructible types</li> <li>fix: restore user-warnings within the library implementation</li> <li>fix: the text symbol of <code>foot_pound_force</code> and <code>foot_pound_force_per_second</code></li> <li>fix: quantity modulo arithmetics fixed</li> <li>(!) build: Conan testing version is now hosted on Artifactory</li> <li>(!) build: Linear Algebra is now hosted on its Artifactory</li> <li>(!) build: <code>BUILD_DOCS</code> CMake option renamed to <code>UNITS_BUILD_DOCS</code></li> <li>build: doxygen updated to 1.8.20</li> <li>build: catch2 updated to 2.13.4</li> <li>build: fmt updated to 7.1.3</li> <li>build: ms-gsl replaced with gsl-lite/0.38.0</li> <li>build: Conan generator switched to <code>cmake_find_package_multi</code></li> <li>build: Conan CMakeToolchain support added</li> <li>build: CMake scripts cleanup</li> <li>build: ccache support added</li> <li>ci: CI switched from Travis CI to GitHub Actions</li> </ul>"},{"location":"appendix/release_notes/#0.6.0","title":"0.6.0 September 13, 2020","text":"<ul> <li>feat: <code>quantity_point</code> support added (thanks @johelegp)</li> <li>feat: Added angle as SI base dimension (thanks @kwikius)</li> <li>feat: <code>si::angular_velocity</code> support added (thanks @mikeford3)</li> <li>feat: FPS system added (thanks @mikeford3)</li> <li>feat: Added support for mathematical function <code>exp(quantity)</code></li> <li>feat: Localization support for text output added (thanks @rbrugo)</li> <li>feat: Added STL random number distribution wrappers (thanks @yasamoka)</li> <li>(!) refactor: Refactored and cleaned up the library file tree</li> <li>(!) refactor: <code>q_*</code> UDL renamed to <code>_q_*</code></li> <li>(!) refactor: UDLs with \"per\" in name renamed from <code>*p*</code> to <code>*_per_*</code></li> <li>(!) refactor: <code>ratio</code> changed to the NTTP kind</li> <li>(!) refactor: <code>exp</code> and <code>Exp</code> renamed to <code>exponent</code> and <code>Exponent</code></li> <li>(!) refactor: <code>Scalar</code> concept renamed to <code>ScalableNumber</code></li> <li>(!) refactor: Dimensionless quantities redesigned to be of a <code>quantity</code> type</li> <li>refactor: <code>math.h</code> function signatures refactored to use a <code>Quantity</code> concept (thanks @kwikius)</li> <li>refactor: <code>[[nodiscard]]</code> added to many functions</li> <li>fix: <code>si::day</code> unit symbol fixed to <code>d</code> (thanks @komputerwiz)</li> <li>fix: <code>si::mole</code> unit symbol fixed to <code>mol</code> (thanks @mikeford3)</li> <li>(!) build: gcc-9 is no longer supported (at least gcc-10 is required)</li> <li>build: Visual Studio 16.7 support added</li> <li>build: linear_algebra updated to 0.7.0/stable</li> <li>build: fmt updated to 7.0.3</li> <li>build: range-v3 updated to 0.11.0</li> <li>build: catch2 updated to 2.13.0</li> <li>build: doxygen updated to 1.8.18</li> <li>build: ms-gsl 3.1.0 dependency added</li> <li>build: Removed the dependency on a git submodule with common CMake scripts</li> </ul>"},{"location":"appendix/release_notes/#0.5.0","title":"0.5.0 May 17, 2020","text":"<ul> <li>Major refactoring and rewrite of the library</li> <li>Units are now independent from dimensions</li> <li>Dimensions now depend on units (base or coherent units are provided in a class template)</li> <li>Quantity gets a Dimension template parameter again (as unit does not provide information about its dimension anymore)</li> <li>Spaceship operator support added</li> <li>Added official CGS system support</li> <li>Added official data information system support</li> <li>Repository file tree cleanup</li> <li><code>ratio</code> refactored to contain <code>Exp</code> template parameter (thanks a lot @oschonrock!)</li> <li>SI fundamental constants added</li> <li><code>q_</code> prefix applied to all the UDLs (thanks @kwikius)</li> <li><code>unknown_unit</code> renamed to <code>unknown_coherent_unit</code></li> <li>Project documentation greatly extended and switched to Sphinx</li> <li>A few more usage examples added</li> <li>ASCII-only output support added (thanks @yasamoka)</li> <li>Representation values formatting extended (thanks @rbrugo)</li> <li>Output streams formatting support added</li> <li>Linear algebra from <code>std::experimental::math</code> support added</li> <li>Named SI units and their dimensions added (thanks @rbrugo</li> <li>libfmt updated to 6.2.0</li> <li>Added absolute functions and epsilon to math.h (thanks @mikeford3)</li> <li>Added a lot of prefixes to named units and introduced <code>alias_unit</code> (thanks @yasamoka)</li> <li>Linking with Conan targets only when they exists (#98)</li> <li>All physical dimensions and units put into <code>physical</code> namespace</li> <li>CMake improvements</li> <li>Velocity renamed to speed</li> </ul> <p>Many thanks to GitHub users @oschonrock, @kwikius, and @i-ky for their support in drafting a new library design.</p>"},{"location":"appendix/release_notes/#0.4.0","title":"0.4.0 Nov 17, 2019","text":"<ul> <li>Support for derived dimensions in <code>exp</code> added</li> <li>Added <code>pow()</code> and <code>sqrt()</code> operations on quantities</li> <li><code>units</code> removed from a <code>std::experimental</code> namespace</li> <li>Downcasting facility refactored so the user does not have to write the boilerplate code anymore</li> <li>From now on base dimensions should inherit from <code>base_dimension</code> class template</li> <li>Added unit symbols definitions to <code>base_dimension</code> and derived units</li> <li>Added support for <code>operator&lt;&lt;</code> on <code>quantity</code></li> <li><code>fmt</code> support added</li> <li>Derived unit factory helpers refactored</li> <li>Refactored the way prefixed units are defined</li> </ul>"},{"location":"appendix/release_notes/#0.3.1","title":"0.3.1 Sep 18, 2019","text":"<ul> <li>cmcstl2 dependency changed to range-v3 0.9.1</li> </ul>"},{"location":"appendix/release_notes/#0.3.0","title":"0.3.0 Sep 16, 2019","text":"<ul> <li>The design as described on CppCon 2019 talk (https://youtu.be/0YW6yxkdhlU)</li> <li>Applied the feedback from the Cologne evening session</li> <li><code>upcasting_traits</code> renamed to <code>downcasting_traits</code></li> <li><code>Dimension</code> template parameter removed from quantity</li> <li><code>units</code> moved to a <code>std::experimental</code> namespace</li> <li>Leading underscore prefix removed from UDLs</li> <li>Added a few more derived dimensions</li> <li><code>meter</code> renamed to <code>metre</code></li> <li>Missing <code>operator*</code> added</li> <li>Predefined dimensions moved to a dedicated directory</li> <li><code>dimension_</code> prefix removed from names of derived dimensions</li> <li>cmcstl2 library updated to 2019.09.19</li> <li><code>base_dimension</code> is a value provided as <code>const&amp;</code> to the <code>exp</code> type</li> <li>integrated with Compiler Explorer</li> <li>gsl-lite dependency removed</li> <li>Fractional dimension exponents support added</li> <li><code>QuantityOf</code> concept introduced</li> <li><code>quantity_cast&lt;U, Rep&gt;()</code> support added</li> </ul>"},{"location":"appendix/release_notes/#0.2.0","title":"0.2.0 July 18, 2019","text":"<ul> <li>The design as described on C++Now 2019 talk (https://youtu.be/wKchCktZPHU)</li> <li>Added C++20 features supported by gcc-9.1 (<code>std::remove_cvref_t</code>, down with typename, <code>std::type_identity</code>)</li> <li>Compile-time performance optimizations (<code>type_list</code>, <code>common_ratio</code>, <code>ratio</code>, <code>conditional_t</code>)</li> </ul>"},{"location":"appendix/release_notes/#0.1.0","title":"0.1.0 May 18, 2019","text":"<ul> <li>Initial library release</li> <li>Begin semantic versioning</li> <li>The last version to work with gcc-8</li> </ul>"},{"location":"getting_started/code_example/","title":"Code Example","text":"<p>Here is a small example of operations possible on scalar quantities:</p> <pre><code>#include &lt;mp-units/systems/si/si.h&gt;\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n// simple numeric operations\nstatic_assert(10 * km / 2 == 5 * km);\n// unit conversions\nstatic_assert(1 * h == 3600 * s);\nstatic_assert(1 * km + 1 * m == 1001 * m);\n// derived quantities\ninline constexpr auto kmph = km / h;\nstatic_assert(1 * km / (1 * s) == 1000 * (m / s));\nstatic_assert(2 * kmph * (2 * h) == 4 * km);\nstatic_assert(2 * km / (2 * kmph) == 1 * h);\nstatic_assert(2 * m * (3 * m) == 6 * m2);\nstatic_assert(10 * km / (5 * km) == 2 * one);\nstatic_assert(1000 / (1 * s) == 1 * kHz);\n</code></pre> <p>Try it on Compiler Explorer</p> <p>This library requires some C++20 features (concepts and constraints, classes as NTTP, ...). Thanks to them, the user gets a powerful but still easy-to-use interface where all unit conversions and dimensional analysis can be performed without sacrificing accuracy. Please see the below example for a quick preview of basic library features:</p> <pre><code>#include &lt;mp-units/format.h&gt;\n#include &lt;mp-units/ostream.h&gt;\n#include &lt;mp-units/systems/international/international.h&gt;\n#include &lt;mp-units/systems/isq/isq.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;iostream&gt;\nusing namespace mp_units;\nconstexpr QuantityOf&lt;isq::speed&gt; auto avg_speed(QuantityOf&lt;isq::length&gt; auto d,\nQuantityOf&lt;isq::time&gt; auto t)\n{\nreturn d / t;\n}\nint main()\n{\nusing namespace mp_units::si::unit_symbols;\nusing namespace mp_units::international::unit_symbols;\nconstexpr quantity v1 = 110 * (km / h);\nconstexpr quantity v2 = 70 * mph;\nconstexpr quantity v3 = avg_speed(220. * isq::distance[km], 2 * h);\nconstexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * h);\nconstexpr quantity v5 = v3.in(m / s);\nconstexpr quantity v6 = value_cast&lt;m / s&gt;(v4);\nconstexpr quantity v7 = value_cast&lt;int&gt;(v6);\nstd::cout &lt;&lt; v1 &lt;&lt; '\\n'; // 110 km/h\nstd::cout &lt;&lt; v2 &lt;&lt; '\\n'; // 70 mi/h\nstd::cout &lt;&lt; std::format(\"{}\", v3) &lt;&lt; '\\n'; // 110 km/h\nstd::cout &lt;&lt; std::format(\"{:*^14}\", v4) &lt;&lt; '\\n'; // ***70 mi/h****\nstd::cout &lt;&lt; std::format(\"{:%Q in %q}\", v5) &lt;&lt; '\\n'; // 30.5556 in m/s\nstd::cout &lt;&lt; std::format(\"{0:%Q} in {0:%q}\", v6) &lt;&lt; '\\n'; // 31.2928 in m/s\nstd::cout &lt;&lt; std::format(\"{:%Q}\", v7) &lt;&lt; '\\n'; // 31\n}\n</code></pre> <p>Try it on Compiler Explorer</p> <p>Note</p> <p>You can find more code examples in the Examples chapter.</p>"},{"location":"getting_started/faq/","title":"Frequently Asked Questions","text":""},{"location":"getting_started/faq/#why-do-we-spell-metre-instead-of-meter","title":"Why do we spell <code>metre</code> instead of <code>meter</code>?","text":"<p>This is how the BIPM defines it in the SI Brochure (British English spelling by default).</p>"},{"location":"getting_started/faq/#why-dont-we-use-udls-to-create-quantities","title":"Why don't we use UDLs to create quantities?","text":"<p>Many reasons make UDLs a poor choice for a physical units library:</p> <ol> <li>UDLs work only with literals (compile-time known values). Our observation is that besides the unit tests, there are few compile-time known constants used in the production code.</li> <li> <p>Typical implementations of UDLs tend to always use the widest representation type available. In the case of <code>std::chrono::duration</code>, the following is true:</p> <pre><code>using namespace std::chrono_literals;\nauto d1 = 42s;\nauto d2 = 42.s;\nstatic_assert(std::is_same_v&lt;decltype(d1)::rep, std::int64_t&gt;);\nstatic_assert(std::is_same_v&lt;decltype(d2)::rep, long double&gt;);\n</code></pre> </li> <li> <p>While increasing the coverage for the library, we learned that many unit symbols conflict with built-in types or numeric extensions. A few of those are: <code>F</code> (farad), <code>J</code> (joule), <code>W</code> (watt), <code>K</code> (kelvin), <code>d</code> (day), <code>l</code> or <code>L</code> (litre), <code>erg</code>, <code>ergps</code>. For a while for those we used <code>_</code> prefix to make the library work at all, but at some point, we had to unify the naming, and we came up with <code>_q_</code> prefix, which resulted in creating a quantity of a provided unit. So in case the library is standardized, all quantities would be created with UDLs having <code>q_</code> prefix (i.e. <code>42q_s</code>) which is not that nice anymore.</p> </li> <li> <p>UDLs with the same identifiers defined in different namespace can't be disambiguated in the C++ language. If both SI and CGS systems define <code>_q_s</code> UDL for a second unit, then it would not be possible to specify which one to use in case both namespaces are \"imported\".</p> </li> <li> <p>Another bad property of UDLs is that they do not compose. A coherent unit of angular momentum would have a UDL specified as <code>_q_kg_m2_per_s</code>. Now imagine that you want to make every possible user happy. How many variations of that unit would you predefine for differently scaled versions of unit ingredients?</p> </li> <li> <p>UDLs are also really expensive to define and specify. For each unit, we need two definitions. One for integral and another one for floating-point representation. Before the V2 framework, the coherent unit of angular momentum was defined as:</p> <pre><code>constexpr auto operator\"\" _q_kg_m2_per_s(unsigned long long l)\n{\ngsl_ExpectsAudit(std::in_range&lt;std::int64_t&gt;(l));\nreturn angular_momentum&lt;kilogram_metre_sq_per_second, std::int64_t&gt;(static_cast&lt;std::int64_t&gt;(l));\n}\nconstexpr auto operator\"\" _q_kg_m2_per_s(long double l)\n{\nreturn angular_momentum&lt;kilogram_metre_sq_per_second, long double&gt;(l);\n}\n</code></pre> </li> </ol>"},{"location":"getting_started/faq/#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor","title":"Why can't I create a quantity by passing a number to a constructor?","text":"<p>A quantity class template in the mp-units library has no publicly available constructor taking a raw value. Such support is provided by the <code>std::chrono::duration</code> and was pointed out to us as a red flag safety issue by a few parties already.</p> <p>Consider the following structure and a code using it:</p> <pre><code>struct X {\nstd::vector&lt;std::chrono::milliseconds&gt; vec;\n// ...\n};\n</code></pre> <pre><code>X x;\nx.vec.emplace_back(42);\n</code></pre> <p>Everything works fine for years until at some point someone changes the structure to:</p> <pre><code>struct X {\nstd::vector&lt;std::chrono::microseconds&gt; vec;\n// ...\n};\n</code></pre> <p>The code continues to compile just fine but all the calculations are off now. This is why we decided to not follow this path.</p> <p>In the mp-units library, both a number and a unit have to always be explicitly provided in order to form a quantity.</p>"},{"location":"getting_started/faq/#why-60-km-h-does-not-compile","title":"Why <code>60 * km / h</code> does not compile?","text":"<p>The library design does not allow multiplying or dividing a quantity (the result of <code>60 * km</code>) by another unit. This significantly limits the number of possible errors and surprises in the quantity equations.</p> <p>Consider the following expression:</p> <pre><code>auto q = 60 * km / 2 * h;\n</code></pre> <p>Looks like <code>30 km/h</code>, right? But it is not. If the above code was allowed, it would result in <code>30 km\u22c5h</code>. In case you want to divide <code>60 * km</code> by <code>2 * h</code> a parenthesis is needed:</p> <pre><code>auto q = 60 * km / (2 * h);\n</code></pre> <p>Another surprising issue could result from the following code:</p> <pre><code>template&lt;typename T&gt;\nauto make_length(T v) { return v * si::metre; }\nauto v = 42;\nauto q = make_length(v);\n</code></pre> <p>The above might look like a good idea, but consider what would happen in the user provided an already existing quantity:</p> <pre><code>auto v = 42 * m;\nauto q = make_length(v);\n</code></pre> <p>Fortunately, with the current design, such issues are detected at compile-time as multiplying or dividing a quantity by a unit is not be supported.</p>"},{"location":"getting_started/faq/#why-a-dimensionless-quantity-is-not-just-a-fundamental-arithmetic-type","title":"Why a dimensionless quantity is not just a fundamental arithmetic type?","text":"<p>In the initial design of this library, the resulting type of division of two quantities was their common representation type:</p> <pre><code>static_assert(std::is_same_v&lt;decltype(10 * km / (5 * km)), std::int64_t&gt;);\n</code></pre> <p>First of all, this was consistent with <code>std::chrono::duration</code> behavior. Additional reasoning behind it was not providing a false impression of a strong <code>quantity</code> type for something that looks and feels like a regular number. Also, all of the mathematic and trigonometric functions were working fine out of the box with such representation types, so we did not have to rewrite <code>sin()</code>, <code>cos()</code>, <code>exp()</code>, and others.</p> <p>However, the feedback we got from the production usage was that such an approach is really bad for generic programming. It is hard to handle the result of the two quantities' division (or multiplication) as it might be either a quantity or a fundamental type. If we want to raise such a result to some power, we must use <code>units::pow</code> or <code>std::pow</code> depending on the resulting type. Those are only a few issues related to such an approach.</p> <p>Moreover, suppose you divide quantities of the same dimension but with units of significantly different magnitudes. In that case, you may end up with a really small or a huge floating-point value, which may result in losing lots of precision. Returning a dimensionless quantity from such cases allows us to benefit from all the properties of scaled units and is consistent with the rest of the library.</p> <p>Note</p> <p>More information on the current design can be found in the Dimensionless Quantities chapter.</p>"},{"location":"getting_started/faq/#why-unicode-quantity-symbols-are-used-by-default-instead-of-ascii-only-characters","title":"Why Unicode quantity symbols are used by default instead of ASCII-only characters?","text":"<p>Both C++ and ISO 80000 are standardized by the ISO. ISO 80000 and the SI standards specify Unicode symbols as the official unit names for some quantities (i.e. <code>\u03a9</code> symbol for the resistance quantity). As mp-units library will be proposed for standardization as a part of the C++ Standard Library we have to obey the rules and be consistent with ISO specifications.</p> <p>Note</p> <p>We do understand engineering reality and the constraints of some environments. This is why the library has the option of ASCII-only Quantity Symbols.</p>"},{"location":"getting_started/faq/#why-dont-you-have-cmake-options-to-disable-the-building-of-tests-and-examples","title":"Why don't you have CMake options to disable the building of tests and examples?","text":"<p>Over time many people provided PRs proposing adding options to build tests and examples conditionally. Here are a few examples:</p> <ul> <li>Add CMake options for disabling docs, examples and tests</li> <li>build: add options to disable part of the build</li> <li>CMake Refactoring and Option Cleanup</li> </ul> <p>We admit this is a common practice in the industry, but we also believe this is a bad pattern.</p> <p>First, the only need for such options comes when a user wants to use <code>add_subdirectory()</code> in CMake to handle dependencies. Such an approach does not scale and should be discouraged. There is little use for such a practice in times when we have dedicated package managers like Conan.</p> <p>The second thing is that our observation is that many people are fixed on disabling \"unneeded\" subdirectories from compilation, but they do not see or address the biggest issue, which is polluting user's build environment with our development-specific settings. Propagating our restrictive compilation flags to user's project is not the best idea as it might cause a lot of harm if this project stops to compile because of that.</p> <p>Last but not least, not having those options is on purpose. Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for project's development. We want to ensure that everyone will build ALL the code correctly before pushing a commit. Having such options would allow unintended issues to leak to PRs and CI.</p> <p>This is why our projects have two entry points:</p> <ul> <li>./CMakeLists.txt is to be used by projects developers to build ALL the project code with really restrictive compilation flags,</li> <li>./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's <code>add_subdirectory()</code> to handle the dependencies.</li> </ul> <p>Note</p> <p>For more details on this please refer to the CMake + Conan: 3 Years Later - Mateusz Pusz lecture that Mateusz Pusz provided at the C++Now 2021 conference.</p>"},{"location":"getting_started/installation_and_usage/","title":"Installation And Usage","text":"<p>Info</p> <p>mp-units library tries to provide the best user experience possible with the C++ language. To achieve that, it extensively uses C++20 features and the explicit object parameter from C++23.</p> <p>Even though the library benefits from C++23 (if available), C++20 is enough to compile and use all of the library's functionality. C++23 features are hidden behind a preprocessor macro providing a backward-compatible way to use it.</p> <p>As of today, the library compiles fine on the following compilers (or newer):</p> <ul> <li>gcc-11<ul> <li>please note that we observed some ICEs on gcc-11</li> <li>no problems with gcc-12.2+</li> </ul> </li> <li>clang-16</li> </ul>"},{"location":"getting_started/installation_and_usage/#repository-structure-and-dependencies","title":"Repository Structure and Dependencies","text":"<p>This repository contains three independent CMake-based projects:</p> <ul> <li> <p>./src</p> <ul> <li>header-only project containing whole mp-units library</li> <li>./src/CMakeList.txt file is intended as an entry point for library users</li> <li> <p>in case this library becomes part of the C++ standard, it will have no external dependencies but until then, it depends on the following:</p> <ul> <li>gsl-lite to verify runtime contracts with the <code>gsl_Expects</code> macro,</li> <li>{fmt} to provide text formatting of quantities (if <code>std::format</code> is not supported yet on a specific compiler),</li> <li>[only for clang &lt; 14 with libc++] range-v3 to provide needed C++20 concepts and utilities.</li> </ul> </li> </ul> </li> <li> <p>.</p> <ul> <li>project used as an entry point for library development and CI/CD</li> <li>it wraps ./src project together with usage examples and tests</li> <li> <p>additionally to the dependencies of ./src project, it uses:</p> <ul> <li><code>Catch2 &lt;https://github.com/catchorg/Catch2&gt;</code>_ library as a unit tests framework,</li> <li><code>linear algebra &lt;https://github.com/BobSteagall/wg21/tree/master/include&gt;</code> library based on proposal <code>P1385 &lt;https://wg21.link/P1385&gt;</code> used in some examples and tests.</li> </ul> </li> <li> <p>in case you also want to generate the project's documentation, you will need:</p> <ul> <li>Material for MkDocs</li> </ul> </li> </ul> </li> <li> <p>./test_package</p> <ul> <li>library installation and Conan package verification.</li> </ul> </li> </ul> <p>Tip</p> <p>Top level CMakeLists.txt file should only be used by mp-units developers and contributors as an entry point for the project's development. We want to ensure that everyone will build ALL the code correctly before pushing a commit. Having such options would allow unintended issues to leak to PRs and CI.</p> <p>This is why our projects have two entry points:</p> <ul> <li>./CMakeLists.txt is to be used by projects developers to build ALL the project code with really restrictive compilation flags,</li> <li>./src/CMakeLists.txt contains only a pure library definition and should be used by the customers that prefer to use CMake's <code>add_subdirectory()</code> to handle the dependencies.</li> </ul> <p>To learn more about the rationale, please check our FAQ.</p>"},{"location":"getting_started/installation_and_usage/#obtaining-dependencies","title":"Obtaining Dependencies","text":"<p>This library assumes that most of the dependencies will be provided by the Conan Package Manager. If you want to obtain required dependencies by other means, some modifications to the library's CMake files might be needed. The rest of the dependencies responsible for documentation generation are provided by <code>python3-pip</code>.</p>"},{"location":"getting_started/installation_and_usage/#conan-quick-intro","title":"Conan Quick Intro","text":"<p>In case you are not familiar with Conan, to install it (or upgrade) just do:</p> <pre><code>pip3 install -U conan\n</code></pre> <p>After that, you might need to add a custom profile file for your development environment in ~/.conan2/profiles directory. An example profile can look as follows:</p> ~/.conan2/profiles/gcc12<pre><code>[settings]\narch=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.cppstd=20\ncompiler.libcxx=libstdc++11\ncompiler.version=12\nos=Linux\n[conf]\ntools.build:compiler_executables={\"c\": \"gcc-12\", \"cpp\": \"g++-12\"}\n</code></pre> <p>Tip</p> <p>Please note that the mp-units library requires C++20 to be set in a Conan profile or forced via the Conan command line. If you do the former, you will not need to provide <code>-s compiler.cppstd=20</code> every time your run a Conan command line (as provided in the command line instructions below).</p> <p>Additionally, it is recommended to set Ninja as a CMake generator for Conan. To do so, you should create a ~/.conan2/global.conf file that will set <code>tools.cmake.cmaketoolchain:generator</code> to one of Ninja generators. For example:</p> ~/.conan2/global.conf<pre><code>tools.cmake.cmaketoolchain:generator=\"Ninja Multi-Config\"\n</code></pre> <p>Info</p> <p>~/.conan2/global.conf file may also set <code>tools.cmake.cmake_layout:build_folder_vars</code> which makes working with several compilers or build configurations easier. For example, the below line will force Conan to generate separate CMake presets and folders for each compiler:</p> ~/.conan2/global.conf<pre><code>tools.cmake.cmake_layout:build_folder_vars=[\"settings.compiler\", \"settings.compiler.version\"]\n</code></pre> <p>In such a case, you will need to use a configuration-specific preset name in the Conan instructions provided below rather then just <code>conan-default</code> and <code>conan-release</code> (i.e. <code>conan-gcc-11</code> and <code>conan-gcc-11-release</code>)</p>"},{"location":"getting_started/installation_and_usage/#build-options","title":"Build Options","text":""},{"location":"getting_started/installation_and_usage/#conan-configuration-properties","title":"Conan Configuration Properties","text":"<code>user.build:all</code> <p> 0.8.0 \u00b7 <code>True</code>/<code>False</code> (Default: <code>False</code>)</p> <p>Enables compilation of all the source code including tests and examples. To support this it requires some additional Conan build dependencies described in Repository Structure and Dependencies. It also runs unit tests during Conan build (unless <code>tools.build:skip_test</code> configuration property is set to <code>True</code>).</p> <code>user.build:skip_la</code> <p> 0.8.0 \u00b7 <code>True</code>/<code>False</code> (Default: <code>False</code>)</p> <p>If <code>user.build:all</code> is enabled, among others, Conan installs the external wg21-linear_algebra dependency and enables the compilation of linear algebra-based tests and usage examples. Such behavior can be disabled with this option.</p>"},{"location":"getting_started/installation_and_usage/#cmake-options","title":"CMake Options","text":"<code>MP_UNITS_AS_SYSTEM_HEADERS</code> <p> 2.0.0 \u00b7 <code>ON</code>/<code>OFF</code> (Default: <code>OFF</code>)</p> <p>Exports library as system headers.</p> <code>MP_UNITS_BUILD_LA</code> <p> 2.0.0 \u00b7 <code>ON</code>/<code>OFF</code> (Default: <code>ON</code>)</p> <p>Enables building code depending on the linear algebra library.</p> <code>MP_UNITS_IWYU</code> <p> 2.0.0 \u00b7 <code>ON</code>/<code>OFF</code> (Default: <code>OFF</code>)</p> <p>Enables <code>include-what-you-use</code> when compiling with a clang compiler. Additionally turns on <code>MP_UNITS_AS_SYSTEM_HEADERS</code>.</p> <code>MP_UNITS_USE_LIBFMT</code> <p> 2.0.0 \u00b7 <code>ON</code>/<code>OFF</code> (Default: <code>ON</code>)</p> <p>Enables usage of {fmt} library instead of the C++20 Standard Library feature.</p>"},{"location":"getting_started/installation_and_usage/#cmake-with-presets-support","title":"CMake with Presets Support","text":"<p>It is recommended to use at least CMake 3.23 to build this project as this version introduced support for CMake Presets schema version 4 used now by Conan to generate presets files. All build instructions below assume that you have such support. If not, your CMake invocations have to be replaced with something like:</p> <pre><code>mkdir build &amp;&amp; cd build\ncmake .. -G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=&lt;path_to_generators_dir&gt;/conan_toolchain.cmake\ncmake --build . --config Release\n</code></pre> <p>Tip</p> <p>In case you can't use CMake 3.23 but you have access to CMake 3.20 or later, you can append <code>-c tools.cmake.cmaketoolchain.presets:max_schema_version=2</code> to the <code>conan install</code> command which will force Conan to use an older version of the CMake Presets schema.</p>"},{"location":"getting_started/installation_and_usage/#installation-and-reuse","title":"Installation and Reuse","text":"<p>There are many different ways of installing/reusing mp-units in your project. Below we mention only a few of many options possible.</p> <p>Important</p> <p>The easiest and most recommended way to obtain mp-units is with the Conan package manager. See Conan + CMake (release) for a detailed instruction.</p>"},{"location":"getting_started/installation_and_usage/#copy","title":"Copy","text":"<p>As mp-units is a C++ header-only library you can simply copy all needed src/*/include subdirectories to your source tree.</p> <p>Note</p> <p>In such a case, you are on your own to ensure all the dependencies are installed, and their header files can be located during the build. Please also note that some compiler-specific flags are needed to make the code compile without issues.</p>"},{"location":"getting_started/installation_and_usage/#copy-cmake","title":"Copy + CMake","text":"<p>If you copy the whole mp-units repository to your project's file tree, you can reuse CMake targets defined by the library. To do so, you should use CMakeLists.txt file from the ./src directory:</p> <pre><code>add_subdirectory(&lt;path_to_units_folder&gt;/src)\n# ...\ntarget_link_libraries(&lt;your_target&gt; &lt;PUBLIC|PRIVATE|INTERFACE&gt; mp-units::mp-units)\n</code></pre> <p>Note</p> <p>You are still on your own to make sure all the dependencies are installed and their header and CMake configuration files can be located during the build.</p>"},{"location":"getting_started/installation_and_usage/#conan-cmake-release","title":"Conan + CMake (release)","text":"<p>Tip</p> <p>If you are new to the Conan package manager, it is highly recommended to read Obtaining Dependencies and refer to Consuming packages chapter of the official Conan documentation for more information.</p> <p>mp-units releases are hosted on Conan-Center. To obtain an official library release, the following steps may be performed:</p> <ol> <li> <p>Create Conan configuration file (either conanfile.txt or conanfile.py) in your project's top-level directory and add mp-units as a dependency of your project. For example, the simplest file may look as follows:</p> conanfile.txt<pre><code>[requires]\nmp-units/2.0.0\n[layout]\ncmake_layout\n[generators]\nCMakeToolchain\nCMakeDeps\n</code></pre> </li> <li> <p>Import mp-units and its dependencies definitions to your project's build procedure with <code>find_package</code>:</p> <pre><code>find_package(mp-units CONFIG REQUIRED)\n</code></pre> </li> <li> <p>Link your CMake targets with mp-units:</p> <pre><code>target_link_libraries(&lt;your_target&gt; &lt;PUBLIC|PRIVATE|INTERFACE&gt; mp-units::mp-units)\n</code></pre> </li> <li> <p>Download, build, and install Conan dependencies before running the CMake configuration step:</p> <pre><code>conan install . -pr &lt;your_conan_profile&gt; -s compiler.cppstd=20 -b=missing\ncmake --preset conan-default\ncmake --build --preset conan-release\n</code></pre> </li> </ol>"},{"location":"getting_started/installation_and_usage/#conan-cmake-live-at-head","title":"Conan + CMake (Live At Head)","text":"<p>This chapter describes the procedure to Live At Head, which means using the latest version of mp-units all the time.</p> <p>Note</p> <p>Please note that even though the Conan packages that you will be using are generated ONLY for builds that are considered stable (passed our CI tests) some minor regressions may happen (our CI and C++20 build environment is not perfect yet). Also, please expect that the library interface might, and probably will, change occasionally. Even though we do our best, such changes might not be reflected in the project's documentation right away.</p> <p>The procedure is similar to the one described in Conan + CMake (release) with the following differences:</p> <ol> <li> <p>Before starting the previous procedure, add mp-units remote to your Conan configuration:</p> <pre><code>conan remote add conan-mpusz https://mpusz.jfrog.io/artifactory/api/conan/conan-oss\n</code></pre> </li> <li> <p>In your Conan configuration file, provide the package identifier of the <code>mpusz/testing</code> stream:</p> conanfile.txt<pre><code>[requires]\nmp-units/2.1.0@mpusz/testing\n[layout]\ncmake_layout\n[generators]\nCMakeToolchain\nCMakeDeps\n</code></pre> <p>Tip</p> <p>The identifiers of the latest packages can always be found in the project's README file or on the project's Artifactory.</p> </li> <li> <p>Force Conan to check for updated recipes with <code>-u</code>:</p> <pre><code>conan install . -pr &lt;your_conan_profile&gt; -s compiler.cppstd=20 -b=missing -u\n</code></pre> </li> </ol>"},{"location":"getting_started/installation_and_usage/#install","title":"Install","text":"<p>In case you don't want to use Conan in your project and just want to install the mp-units library on your file system and use <code>find_package(mp-units)</code> from another repository to find it, it is enough to perform the following steps:</p> <pre><code>conan install . -pr &lt;your_conan_profile&gt; -s compiler.cppstd=20 -b=missing\nmv CMakeUserPresets.json src\ncd src\ncmake --preset conan-default -DCMAKE_INSTALL_PREFIX=&lt;your_installation_path&gt;\ncmake --build --preset conan-release --target install\n</code></pre>"},{"location":"getting_started/installation_and_usage/#contributing-or-just-building-all-the-tests-and-examples","title":"Contributing (or just building all the tests and examples)","text":"<p>In case you would like to build all the mp-units source code (with unit tests and examples), you should:</p> <ol> <li>Use the CMakeLists.txt from the top-level directory.</li> <li>Run Conan with <code>user.build:all</code> = <code>True</code>.</li> </ol> <pre><code>git clone https://github.com/mpusz/mp-units.git &amp;&amp; cd units\nconan build . -pr &lt;your_conan_profile&gt; -s compiler.cppstd=20 -c user.build:all=True -b missing\n</code></pre> <p>The above will download and install all of the dependencies needed for the development of the library, build all of the source code, and run unit tests.</p> <p>If you prefer to build the project via CMake rather than Conan, then you should replace the <code>conan build</code> with <code>conan install</code> command and then follow with a regular CMake build:</p> <pre><code>cmake --preset conan-default\ncmake --build --preset conan-release\ncmake --build --preset conan-release --target test\n</code></pre>"},{"location":"getting_started/installation_and_usage/#building-documentation","title":"Building documentation","text":"<p>Starting from mp-units 2.0 we are using Material for MkDocs to build our documentation. The easiest way to install all the required dependencies is with <code>pip</code>:</p> <pre><code>pip install -U mkdocs-material\n</code></pre> <p>After that, you can either:</p> <ul> <li> <p>easily start a live server to preview the documentation as you write</p> <pre><code>mkdocs serve\n</code></pre> </li> <li> <p>build the documentation</p> <pre><code>mkdocs build\n</code></pre> </li> </ul>"},{"location":"getting_started/installation_and_usage/#packaging","title":"Packaging","text":"<p>To test CMake installation and Conan packaging or create a Conan package run:</p> <pre><code>conan create . &lt;username&gt;/&lt;channel&gt; -pr &lt;your_conan_profile&gt; -s compiler.cppstd=20 -c user.build:all=True -b missing\n</code></pre> <p>The above will create a Conan package and run tests provided in ./test_package directory.</p>"},{"location":"getting_started/installation_and_usage/#uploading-mp-units-package-to-the-conan-server","title":"Uploading mp-units Package to the Conan Server","text":"<pre><code>conan upload -r &lt;remote-name&gt; --all mp-units/2.0.0@&lt;user&gt;/&lt;channel&gt;\n</code></pre>"},{"location":"getting_started/introduction/","title":"Introduction","text":"<p>mp-units is a Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The initial versions of the library were inspired by the <code>std::chrono::duration</code> but with each release, the interfaces diverged from the original to provide a better user experience.</p> <p>Info</p> <p>A brief introduction to the library's interfaces and the rationale for changes in the version 2.0 of mp-units were provided in detail by Mateusz Pusz in the \"mp-units: Lessons Learned and a New C++ Library Design\" talk at the ACCU 2023 conference.</p>"},{"location":"getting_started/introduction/#open-source","title":"Open Source","text":"<p>mp-units is Free and Open Source, with a permissive MIT license. Check out the source code and issue tracking (for questions and support, reporting bugs, suggesting feature requests and improvements) at https://github.com/mpusz/mp-units.</p>"},{"location":"getting_started/introduction/#with-the-users-experience-in-mind","title":"With the User's Experience in Mind","text":"<p>Most of the critical design decisions in the library are dictated by the requirement of providing the best user experience possible. Other C++ physical units libraries are \"famous\" for their enormous and hard-to-understand error messages (one line of the error log often do not fit on one slide). The ultimate goal of mp-units is to improve this and make compile-time errors and debugging as easy and user-friendly as possible.</p> <p>To achieve this goal, several techniques are applied:</p> <ul> <li>usage of C++20 concepts,</li> <li>using strong types for framework entities (instead of type aliases),</li> <li>usage of expression templates to improve the readability of generated types,</li> <li>limiting the number of template arguments to the bare minimum.</li> </ul> <p>Important</p> <p>In many generic C++ libraries compile-time errors do not happen often. It is hard to break <code>std::string</code> or <code>std::vector</code> in a way it won't compile with a huge error log. Physical Units libraries are different. Generation of compile-time errors is the main reason to use such a library.</p>"},{"location":"getting_started/introduction/#key-features","title":"Key Features","text":"Feature Description Safety - The affine space strong types (<code>quantity</code> and <code>quantity_point</code>)- Compile-time checked conversions of quantities and units- Unique support for many quantities of the same kind- Type-safe equations on scalar, vector, and tensor quantities and their units- Value-preserving conversions Performance - All the compile-time logic implemented as immediate (<code>consteval</code>) functions- As fast or even faster than working with fundamental types- No space size overhead needed to implement high-level abstractions Great User Experience - Optimized for readable compilation errors and great debugging experience- Efficient and composable way to specify a unit of choice- Value-based dimension, unit, and quantity equations Feature Rich - Systems of Quantities- Systems of Units- Scalar, vector, and tensor quantities- The affine space- Natural units systems support- Strong angular system- Supports any unit's magnitude (huge, small, floating-point)- Faster-than-lightspeed constants- Highly adjustable text-output formatting Easy to Extend - Each entity can be defined with a single line of code- User can easily extend the systems with custom dimensions, quantities, and units Low Standardization Cost - Small number of predefined entities needed thanks to composability- No external dependencies (assuming full C++20 support)- No macros in the user interface (besides portability and standard-compliance issues)- Possibility to be standardized as a freestanding part of the C++ Standard Library"},{"location":"getting_started/quick_start/","title":"Quick Start","text":"<p>A quantity is a concrete amount of a unit for a quantity type of a specified dimension with a specific representation, and is represented in the library with a <code>quantity</code> class template.</p>"},{"location":"getting_started/quick_start/#creating-a-quantity","title":"Creating a quantity","text":"<p>The SI Brochure says:</p> <p>SI Brochure</p> <p>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).</p> <p>Following the above, the value of a quantity in the mp-units library is created by multiplying a number with a predefined unit:</p> <pre><code>#include &lt;mp-units/systems/si/si.h&gt;\nusing namespace mp_units;\nquantity q = 42 * si::metre;\n</code></pre> <p>Note</p> <p>The above spelling of <code>metre</code> is not a typo. For motivation, please check our FAQ.</p> <p>The above creates an instance of <code>quantity&lt;si::metre(), int&gt;</code>. The same can be obtained using an optional unit symbol:</p> <pre><code>#include &lt;mp-units/systems/si/si.h&gt;\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\nquantity q = 42 * m;\n</code></pre> <p>Tip</p> <p>Unit symbols introduce a lot of short identifiers into the current namespace, and that is why they are opt-in. A user has to explicitly \"import\" them from a dedicated <code>unit_symbols</code> namespace.</p> <p>In case someone doesn't like the multiply syntax or there is an ambiguity between <code>operator*</code> provided by this and other libraries, a quantity can also be created with a dedicated factory function:</p> <pre><code>#include &lt;mp-units/systems/si/si.h&gt;\nusing namespace mp_units;\nquantity q = make_quantity&lt;si::metre&gt;(42);\n</code></pre>"},{"location":"getting_started/quick_start/#user-provided-unit-wrappers","title":"User-provided unit wrappers","text":"<p>Sometimes it might be awkward to type some derived units:</p> <pre><code>quantity speed = 60 * (km / h);\n</code></pre> <p>Note</p> <p>Please note that <code>60 * km / h</code> will not compile. To read more about the rationale for such a design please check our FAQ.</p> <p>In case such a unit is used a lot in the project, a user can easily provide a nicely named wrapper for it with:</p> <pre><code>constexpr auto kmph = km / h;\nquantity speed = 60 * kmph;\n</code></pre> <p>or even:</p> <pre><code>constexpr auto kilometre = si::kilo&lt;si::metre&gt;;\nconstexpr auto kilometre_per_hour = kilometre / si::hour;\nconstexpr auto kmph = kilometre_per_hour;\nquantity speed = 60 * kmph;\n</code></pre> <p>Note</p> <p>In case you wonder why this library does not use UDLs to create quantities, please check our FAQ.</p>"},{"location":"library_reference/core_library/","title":"Core Library","text":""},{"location":"library_reference/core_library/#concepts","title":"Concepts","text":""},{"location":"library_reference/core_library/#expression-templates","title":"Expression templates","text":""},{"location":"library_reference/core_library/#dimensions","title":"Dimensions","text":""},{"location":"users_guide/terms_and_definitions/","title":"Terms and Definitions","text":"<p>The mp-units project consistently uses the official metrology vocabulary defined by the ISO and BIPM. You can find essential project-related definitions in our documentation's \"Glossary\" chapter. Even more, terms are provided in the official vocabulary of the ISO and BIPM.</p> <p>Tip</p> <p>Please familiarize yourself with terms from \"Glossary\" to better understand the documentation and improve domain-related communication and discussions.</p>"},{"location":"users_guide/examples/avg_speed/","title":"<code>avg_speed</code>","text":"<p>Try it on Compiler Explorer</p> <p>Let's continue the previous example. This time, our purpose will not be to showcase as many library features as possible, but we will scope on different interfaces one can provide with the mp-units. We will also describe some advantages and disadvantages of presented solutions.</p> <p>First, we include all the necessary header files and import all the identifiers from the <code>mp_units</code> namespace:</p> avg_speed.cpp<pre><code>#include &lt;mp-units/ostream.h&gt;\n#include &lt;mp-units/systems/cgs/cgs.h&gt;\n#include &lt;mp-units/systems/international/international.h&gt;\n#include &lt;mp-units/systems/isq/isq.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;exception&gt;\n#include &lt;iostream&gt;\nnamespace {\nusing namespace mp_units;\n</code></pre> <p>Next, we define two functions calculating average speed based on quantities of fixed units and integral and floating-point representation types, respectively, and a third function that we introduced in the previous example:</p> avg_speed.cpp<pre><code>constexpr quantity&lt;si::metre / si::second, int&gt; fixed_int_si_avg_speed(quantity&lt;si::metre, int&gt; d,\nquantity&lt;si::second, int&gt; t)\n{\nreturn d / t;\n}\nconstexpr quantity&lt;si::metre / si::second&gt; fixed_double_si_avg_speed(quantity&lt;si::metre&gt; d, quantity&lt;si::second&gt; t)\n{\nreturn d / t;\n}\nconstexpr QuantityOf&lt;isq::speed&gt; auto avg_speed(QuantityOf&lt;isq::length&gt; auto d, QuantityOf&lt;isq::time&gt; auto t)\n{\nreturn d / t;\n}\n</code></pre> <p>We also added a simple utility to print our results:</p> avg_speed.cpp<pre><code>template&lt;QuantityOf&lt;isq::length&gt; D, QuantityOf&lt;isq::time&gt; T, QuantityOf&lt;isq::speed&gt; V&gt;\nvoid print_result(D distance, T duration, V speed)\n{\nconst auto result_in_kmph = speed.force_in(si::kilo&lt;si::metre&gt; / non_si::hour);\nstd::cout &lt;&lt; \"Average speed of a car that makes \" &lt;&lt; distance &lt;&lt; \" in \" &lt;&lt; duration &lt;&lt; \" is \" &lt;&lt; result_in_kmph\n&lt;&lt; \".\\n\";\n}\n</code></pre> <p>Now, let's analyze how those three utility functions behave with different sets of arguments. First, we are going to use quantities of SI units and integral representation:</p> avg_speed.cpp<pre><code>void example()\n{\nusing namespace mp_units::si::unit_symbols;\n// SI (int)\n{\nconstexpr auto distance = 220 * km;\nconstexpr auto duration = 2 * h;\nstd::cout &lt;&lt; \"SI units with 'int' as representation\\n\";\nprint_result(distance, duration, fixed_int_si_avg_speed(distance, duration));\nprint_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\nprint_result(distance, duration, avg_speed(distance, duration));\n}\n</code></pre> <p>The above provides the following output:</p> <pre><code>SI units with 'int' as representation\nAverage speed of a car that makes 220 km in 2 h is 108 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\n</code></pre> <p>Please note that in the first two cases, we must convert length from <code>km</code> to <code>m</code> and time from <code>h</code> to <code>s</code>. The converted values are used to calculate speed in <code>m / s</code> which is then again converted to the one in <code>km / h</code>. Those conversions not only impact the application's runtime performance but may also affect the final result. Such truncation can be easily observed in the first case where we deal with integral representation types (the resulting speed is <code>108 km / h</code>).</p> <p>The second scenario is really similar to the previous one, but this time, function arguments have floating-point representation types:</p> avg_speed.cpp<pre><code> // SI (double)\n{\nconstexpr auto distance = 220. * km;\nconstexpr auto duration = 2. * h;\nstd::cout &lt;&lt; \"\\nSI units with 'double' as representation\\n\";\n// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\nprint_result(distance, duration, fixed_int_si_avg_speed(value_cast&lt;int&gt;(distance), value_cast&lt;int&gt;(duration)));\nprint_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\nprint_result(distance, duration, avg_speed(distance, duration));\n}\n</code></pre> <p>Conversion from floating-point to integral representation types is considered value-truncating and that is why now, in the first case, we need an explicit call to <code>value_cast&lt;int&gt;</code>.</p> <p>In the text output, we can observe that, again, the resulting value gets truncated during conversions in the first cast:</p> <pre><code>SI units with 'double' as representation\nAverage speed of a car that makes 220 km in 2 h is 108 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\nAverage speed of a car that makes 220 km in 2 h is 110 km/h.\n</code></pre> <p>Next, let's do the same for integral and floating-point representations, but this time using US Customary units:</p> avg_speed.cpp<pre><code> // Customary Units (int)\n{\nusing namespace mp_units::international::unit_symbols;\nconstexpr auto distance = 140 * mi;\nconstexpr auto duration = 2 * h;\nstd::cout &lt;&lt; \"\\nUS Customary Units with 'int' as representation\\n\";\n// it is not possible to make a lossless conversion of miles to meters on an integral type\n// (explicit cast needed)\nprint_result(distance, duration, fixed_int_si_avg_speed(distance.force_in(m), duration));\nprint_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\nprint_result(distance, duration, avg_speed(distance, duration));\n}\n// Customary Units (double)\n{\nusing namespace mp_units::international::unit_symbols;\nconstexpr auto distance = 140. * mi;\nconstexpr auto duration = 2. * h;\nstd::cout &lt;&lt; \"\\nUS Customary Units with 'double' as representation\\n\";\n// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n// also it is not possible to make a lossless conversion of miles to meters on an integral type\n// (explicit cast needed)\nprint_result(distance, duration,\nfixed_int_si_avg_speed(value_cast&lt;int&gt;(distance.force_in(m)), value_cast&lt;int&gt;(duration)));\nprint_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\nprint_result(distance, duration, avg_speed(distance, duration));\n}\n</code></pre> <p>One important difference here is the fact that as it is not possible to make a lossless conversion of miles to meters on a quantity using an integral representation type, so this time, we need a <code>value_cast&lt;si::metre&gt;</code> to force it.</p> <p>If we check the text output of the above, we will see the following:</p> <pre><code>US Customary Units with 'int' as representation\nAverage speed of a car that makes 140 mi in 2 h is 111 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112 km/h.\n\nUS Customary Units with 'double' as representation\nAverage speed of a car that makes 140 mi in 2 h is 111 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\nAverage speed of a car that makes 140 mi in 2 h is 112.654 km/h.\n</code></pre> <p>Please note how the first and third results get truncated using integral representation types.</p> <p>In the end, we repeat the scenario for CGS units:</p> avg_speed.cpp<pre><code> // CGS (int)\n{\nconstexpr auto distance = 22'000'000 * cgs::centimetre;\nconstexpr auto duration = 7200 * cgs::second;\nstd::cout &lt;&lt; \"\\nCGS units with 'int' as representation\\n\";\n// it is not possible to make a lossless conversion of centimeters to meters on an integral type\n// (explicit cast needed)\nprint_result(distance, duration, fixed_int_si_avg_speed(distance.force_in(m), duration));\nprint_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\nprint_result(distance, duration, avg_speed(distance, duration));\n}\n// CGS (double)\n{\nconstexpr auto distance = 22'000'000. * cgs::centimetre;\nconstexpr auto duration = 7200. * cgs::second;\nstd::cout &lt;&lt; \"\\nCGS units with 'double' as representation\\n\";\n// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed\n// it is not possible to make a lossless conversion of centimeters to meters on an integral type\n// (explicit cast needed)\nprint_result(distance, duration,\nfixed_int_si_avg_speed(value_cast&lt;int&gt;(distance.force_in(m)), value_cast&lt;int&gt;(duration)));\nprint_result(distance, duration, fixed_double_si_avg_speed(distance, duration));\nprint_result(distance, duration, avg_speed(distance, duration));\n}\n}\n</code></pre> <p>Again, we observe <code>value_cast</code> being used in the same places and consistent truncation errors in the text output:</p> <pre><code>CGS units with 'int' as representation\nAverage speed of a car that makes 22000000 cm in 7200 s is 108 km/h.\nAverage speed of a car that makes 22000000 cm in 7200 s is 110 km/h.\nAverage speed of a car that makes 22000000 cm in 7200 s is 109 km/h.\n\nCGS units with 'double' as representation\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 108 km/h.\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.\nAverage speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.\n</code></pre> <p>The example file ends with a simple <code>main()</code> function:</p> avg_speed.cpp<pre><code>} // namespace\nint main()\n{\ntry {\nexample();\n} catch (const std::exception&amp; ex) {\nstd::cerr &lt;&lt; \"Unhandled std exception caught: \" &lt;&lt; ex.what() &lt;&lt; '\\n';\n} catch (...) {\nstd::cerr &lt;&lt; \"Unhandled unknown exception caught\\n\";\n}\n}\n</code></pre>","tags":["CGS System","International System","Text Formatting"]},{"location":"users_guide/examples/hello_units/","title":"<code>hello_units</code>","text":"<p>Try it on Compiler Explorer</p> <p>This is a really simple example showcasing the features of the mp-units library.</p> <p>First, we include the headers for:</p> <ul> <li>a system of quantities (ISQ)</li> <li>symbols of SI units</li> <li>symbols of international units</li> <li>text and stream output support</li> </ul> hello_units.cpp<pre><code>#include &lt;mp-units/format.h&gt;\n#include &lt;mp-units/ostream.h&gt;\n#include &lt;mp-units/systems/international/international.h&gt;\n#include &lt;mp-units/systems/isq/isq.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;iostream&gt;\n</code></pre> <p>Also, to shorten the definitions, we \"import\" <code>mp_units</code> namespace.</p> hello_units.cpp<pre><code>using namespace mp_units;\n</code></pre> <p>Next we define a simple function that calculates average speed based on the provided arguments of length and time:</p> hello_units.cpp<pre><code>constexpr QuantityOf&lt;isq::speed&gt; auto avg_speed(QuantityOf&lt;isq::length&gt; auto d, QuantityOf&lt;isq::time&gt; auto t)\n{\nreturn d / t;\n}\n</code></pre> <p>The above function template takes any quantities implicitly convertible to <code>isq::length</code> and <code>isq::time</code> respectively. Those quantities can use any compatible unit and a representation type. The function returns a result of a really simple equation and ensures that its quantity type is implicitly convertible to <code>isq::speed</code>.</p> <p>Tip</p> <p>Besides verifying the type returned from the function, constraining a generic return type is really useful for users of such a function as it provides more information of what to expect from a function than just using <code>auto</code>.</p> hello_units.cpp<pre><code>int main()\n{\nusing namespace mp_units::si::unit_symbols;\nusing namespace mp_units::international::unit_symbols;\n</code></pre> <p>The above lines explicitly opt-in to use 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.</p> hello_units.cpp<pre><code> constexpr quantity v1 = 110 * (km / h);\nconstexpr quantity v2 = 70 * mph;\nconstexpr quantity v3 = avg_speed(220. * km, 2 * h);\nconstexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * isq::duration[h]);\nconstexpr quantity v5 = v3.in(m / s);\nconstexpr quantity v6 = value_cast&lt;m / s&gt;(v4);\nconstexpr quantity v7 = value_cast&lt;int&gt;(v6);\n</code></pre> <ul> <li>Lines <code>16</code> &amp; <code>17</code> create a quantity of kind <code>isq::length / isq::time</code> with the numbers and units provided. Such quantities can be converted or assigned to any other quantity with a matching kind.</li> <li>Line <code>18</code> calls our function template with quantities of kind <code>isq::length</code> and <code>isq::time</code> and number and units provided.</li> <li>Line <code>19</code> 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.</li> <li>Line <code>20</code> changes the unit of a quantity <code>v3</code> to <code>m / s</code> in a value-preserving way (floating-point representations are considered to be value-preserving).</li> <li>Line <code>21</code> does a similar operation but this time it would succeed also for value-truncating cases (if it was the case).</li> <li>Line <code>22</code> does a value-truncating conversion of changing the underlying representation type from <code>double</code> to <code>int</code>.</li> </ul> hello_units.cpp<pre><code> std::cout &lt;&lt; v1 &lt;&lt; '\\n'; // 110 km/h\nstd::cout &lt;&lt; v2 &lt;&lt; '\\n'; // 70 mi/h\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"{}\", v3) &lt;&lt; '\\n'; // 110 km/h\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"{:*^14}\", v4) &lt;&lt; '\\n'; // ***70 mi/h****\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"{:%Q in %q}\", v5) &lt;&lt; '\\n'; // 30.5556 in m/s\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"{0:%Q} in {0:%q}\", v6) &lt;&lt; '\\n'; // 31.2928 in m/s\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"{:%Q}\", v7) &lt;&lt; '\\n'; // 31\n}\n</code></pre> <p>The above presents various ways to print a quantity. Both stream insertion operations and <code>std::format</code> are supported.</p> <p>Tip</p> <p><code>MP_UNITS_STD_FMT</code> is used for compatibility reasons. In case a specific compiler does not support <code>std::format</code> or a user prefers to use <code>{fmt}</code> library, this macro will resolve to <code>fmt</code> namespace. Otherwise, <code>std</code> namespace will be used.</p>","tags":["International System","Text Formatting"]},{"location":"users_guide/examples/si_constants/","title":"<code>si_constants</code>","text":"<p>Try it on Compiler Explorer</p> <p>The next example presents all the seven defining constants of the SI system. We can observe how Faster-than-lightspeed Constants work in practice.</p> si_constants.cpp<pre><code>#include &lt;mp-units/format.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;iostream&gt;\ntemplate&lt;class T&gt;\nrequires mp_units::is_scalar&lt;T&gt;\ninline constexpr bool mp_units::is_vector&lt;T&gt; = true;\n</code></pre> <p>As always, we start with the inclusion of all the needed header files. After that, for the simplicity of this example, we hack the character of quantities to be able to express vector quantities with simple scalar types.</p> si_constants.cpp<pre><code>int main()\n{\nusing namespace mp_units::si;\nusing namespace mp_units::si::unit_symbols;\nstd::cout &lt;&lt; \"The seven defining constants of the SI and the seven corresponding units they define:\\n\";\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\\n\",\n1. * si2019::hyperfine_structure_transition_frequency_of_cs,\n(1. * si2019::hyperfine_structure_transition_frequency_of_cs).in(Hz));\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"- speed of light in vacuum: {} = {:%.0Q %q}\\n\",\n1. * si2019::speed_of_light_in_vacuum,\n(1. * si2019::speed_of_light_in_vacuum).in(m / s));\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"- Planck constant: {} = {:%.8eQ %q}\\n\",\n1. * si2019::planck_constant, (1. * si2019::planck_constant).in(J * s));\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"- elementary charge: {} = {:%.9eQ %q}\\n\",\n1. * si2019::elementary_charge, (1. * si2019::elementary_charge).in(C));\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"- Boltzmann constant: {} = {:%.6eQ %q}\\n\",\n1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant).in(J / K));\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"- Avogadro constant: {} = {:%.8eQ %q}\\n\",\n1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant).in(1 / mol));\nstd::cout &lt;&lt; MP_UNITS_STD_FMT::format(\"- luminous efficacy: {} = {}\\n\",\n1. * si2019::luminous_efficacy, (1. * si2019::luminous_efficacy).in(lm / W));\n}\n</code></pre> <p>The main part of the example prints all of the SI-defining constants. While analyzing the output of this program (provided below), we can easily notice that a direct printing of the quantity provides just a value <code>1</code> with a proper constant symbol. This is the main power of the Faster-than-lightspeed Constants feature. Only after we explicitly convert the unit of a quantity to proper SI units we get an actual numeric value of the constant.</p> <pre><code>The seven defining constants of the SI and the seven corresponding units they define:\n- hyperfine transition frequency of Cs: 1 \u0394\u03bd_Cs = 9192631770 Hz\n- speed of light in vacuum: 1 c = 299792458 m/s\n- Planck constant: 1 h = 6.62607015e-34 J s\n- elementary charge: 1 e = 1.602176634e-19 C\n- Boltzmann constant: 1 k = 1.380649e-23 J/K\n- Avogadro constant: 1 N_A = 6.02214076e+23 1/mol\n- luminous efficacy: 1 K_cd = 683 lm/W\n</code></pre>","tags":["Physical Constants","Text Formatting"]},{"location":"users_guide/examples/tags_index/","title":"Tags Index","text":"<p>Tip</p> <p>All usage examples in this chapter are categorized with appropriate tags to simplify navigation and search of relevant code. You can either read all the examples one-by-one in the order provided by the documentation authors or, thanks to the tagging system, jump straight to the example that is the most interesting for you.</p>"},{"location":"users_guide/examples/tags_index/#cgs-system","title":"CGS System","text":"<ul> <li>avg_speed</li> </ul>"},{"location":"users_guide/examples/tags_index/#international-system","title":"International System","text":"<ul> <li>avg_speed</li> <li>hello_units</li> </ul>"},{"location":"users_guide/examples/tags_index/#physical-constants","title":"Physical Constants","text":"<ul> <li>si_constants</li> </ul>"},{"location":"users_guide/examples/tags_index/#text-formatting","title":"Text Formatting","text":"<ul> <li>avg_speed</li> <li>hello_units</li> <li>si_constants</li> </ul>"},{"location":"users_guide/framework_basics/basic_concepts/","title":"Basic Concepts","text":"<p>The most important concepts in the mp-units library are <code>Dimension</code>, <code>QuantitySpec</code>, <code>Unit</code>, <code>Reference</code>, <code>Representation</code>, <code>Quantity</code>, and <code>QuantityPoint</code>:</p> <pre><code>flowchart TD\n Dimension --- QuantitySpec\n QuantitySpec --- Reference\n Unit --- Reference\n Reference --- Quantity\n Representation --- Quantity\n Quantity --- QuantityPoint\n PointOrigin --- QuantityPoint\n\n click Dimension \"#Dimension\"\n click QuantitySpec \"#QuantitySpec\"\n click Unit \"#Unit\"\n click Reference \"#Reference\"\n click Representation \"#Representation\"\n click Quantity \"#Quantity\"\n click PointOrigin \"#PointOrigin\"\n click QuantityPoint \"#QuantityPoint\"</code></pre>"},{"location":"users_guide/framework_basics/basic_concepts/#Dimension","title":"<code>Dimension&lt;T&gt;</code>","text":"<p><code>Dimension</code> concept matches a dimension of either a base or derived quantity:</p> <ul> <li>Base dimensions are explicitly defined by a user by inheriting from the instantiation of a <code>base_dimension</code> class template. It should be instantiated with a unique symbol identifier describing this dimension in a specific system of quantities.</li> <li>Derived dimensions are implicitly created by the library's framework based on the quantity equation provided in the quantity specification.</li> </ul> Examples <p><code>isq::dim_length</code>, <code>isq::dim_mass</code>, <code>isq::dim_time</code>, <code>isq::dim_electric_current</code>, <code>isq::dim_thermodynamic_temperature</code>, <code>isq::dim_amount_of_substance</code>, and <code>isq::dim_luminous_intensity</code> are the dimensions of base quantities in the ISQ.</p> <p>IEC 80000 provides <code>iec80000::dim_traffic_intensity</code> base dimension to extend ISQ with information technology quantities.</p> <p>A <code>Dimension</code> can be defined by the user in the following way:</p> <pre><code>inline constexpr struct dim_length : base_dimension&lt;\"L\"&gt; {} dim_length;\n</code></pre> <p>The division on quantity specifications also divides their dimensions:</p> <pre><code>static_assert((isq::length / isq::time).dimension == isq::dim_length / isq::dim_time);\n</code></pre> <p>The dimension equation of <code>isq::dim_length / isq::dim_time</code> results in the <code>derived_dimension&lt;isq::dim_length, per&lt;isq::dim_time&gt;&gt;</code> type.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#DimensionOf","title":"<code>DimensionOf&lt;T, V&gt;</code>","text":"<p><code>DimensionOf</code> concept is satisfied when both arguments satisfy a <code>Dimension</code> concept and when they compare equal.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#QuantitySpec","title":"<code>QuantitySpec&lt;T&gt;</code>","text":"<p><code>QuantitySpec</code> concept matches all the quantity specifications including:</p> <ul> <li>Base quantities defined by a user by inheriting from the <code>quantity_spec</code> class template instantiated with a base dimension argument.</li> <li>Derived named quantities defined by a user by inheriting from the <code>quantity_spec</code> class template instantiated with a result of a quantity equation passed as an argument.</li> <li>Other named quantities forming a hierarchy of quantities of the same kind defined by a user by inheriting from the <code>quantity_spec</code> class template instantiated with another \"parent\" quantity specification passed as an argument.</li> <li>Quantity kinds describing a family of mutually comparable quantities.</li> <li>Intermediate derived quantity specifications being a result of a quantity equations on other specifications.</li> </ul> Examples <p><code>isq::length</code>, <code>isq::mass</code>, <code>isq::time</code>, <code>isq::electric_current</code>, <code>isq::thermodynamic_temperature</code>, <code>isq::amount_of_substance</code>, and <code>isq::luminous_intensity</code> are the specifications of base quantities in the ISQ.</p> <p><code>isq::width</code>, <code>isq::height</code>, <code>isq::radius</code>, and <code>isq::position_vector</code> are only a few of many quantities of a kind length specified in the ISQ.</p> <p><code>kind_of&lt;isq::length&gt;</code> behaves as any of the quantities of a kind length.</p> <p><code>isq::area</code>, <code>isq::speed</code>, <code>isq::moment_of_force</code> are only a few of many derived quantities provided in the ISQ.</p> <p><code>QuantitySpec</code> can be defined by the user in one of the following ways:</p> C++23C++20Portable <pre><code>inline constexpr struct length : quantity_spec&lt;dim_length&gt; {} length;\ninline constexpr struct height : quantity_spec&lt;length&gt; {} height;\ninline constexpr struct speed : quantity_spec&lt;length / time&gt; {} speed;\n</code></pre> <pre><code>inline constexpr struct length : quantity_spec&lt;length, dim_length&gt; {} length;\ninline constexpr struct height : quantity_spec&lt;height, length&gt; {} height;\ninline constexpr struct speed : quantity_spec&lt;speed, length / time&gt; {} speed;\n</code></pre> <pre><code>QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(height, length);\nQUANTITY_SPEC(speed, length / time);\n</code></pre> <p>The quantity equation of <code>isq::length / isq::time</code> results in the <code>derived_quantity_spec&lt;isq::length, per&lt;isq::time&gt;&gt;</code> type.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#QuantitySpecOf","title":"<code>QuantitySpecOf&lt;T, V&gt;</code>","text":"<p><code>QuantitySpecOf</code> concept is satisfied when both arguments satisfy a <code>QuantitySpec</code> concept and when <code>T</code> is implicitly convertible to <code>V</code>.</p> More details <p>Additionally:</p> <ul> <li><code>T</code> should not be a nested quantity specification of <code>V</code></li> <li>either <code>T</code> is quantity kind or <code>V</code> should not be a nested quantity specification of <code>T</code></li> </ul> <p>Those additional conditions are required to make the following work:</p> <pre><code>static_assert(ReferenceOf&lt;si::radian, isq::angular_measure&gt;);\nstatic_assert(!ReferenceOf&lt;si::radian, dimensionless&gt;);\nstatic_assert(!ReferenceOf&lt;isq::angular_measure[si::radian], dimensionless&gt;);\nstatic_assert(ReferenceOf&lt;one, isq::angular_measure&gt;);\nstatic_assert(!ReferenceOf&lt;dimensionless[one], isq::angular_measure&gt;);\n</code></pre>"},{"location":"users_guide/framework_basics/basic_concepts/#Unit","title":"<code>Unit&lt;T&gt;</code>","text":"<p><code>Unit</code> concept matches all the units in the library including:</p> <ul> <li>Base units defined by a user by inheriting from the <code>named_unit</code> class template instantiated with a unique symbol identifier describing this unit in a specific system of units.</li> <li>Named scaled units defined by a user by inheriting from the <code>named_unit</code> class template instantiated with a unique symbol identifier and a product of multiplying another unit with some magnitude.</li> <li>Prefixed units defined by a user by inheriting from the <code>prefixed_unit</code> class template instantiated with a prefix symbol, a magnitude, and a unit to be prefixed.</li> <li>Derived named units defined by a user by inheriting from the <code>named_unit</code> class template instantiated with a unique symbol identifier and a result of unit equation passed as an argument.</li> <li>Derived unnamed units being a result of a unit equations on other units.</li> </ul> <p>Note</p> <p>In the mp-units library, physical constants are also implemented as units.</p> Examples <p><code>si::second</code>, <code>si::metre</code>, <code>si::kilogram</code>, <code>si::ampere</code>, <code>si::kelvin</code>, <code>si::mole</code>, and <code>si::candela</code> are the base units of SI.</p> <p><code>si::kilo&lt;si::metre&gt;</code> is a prefixed unit on length.</p> <p><code>si::radian</code>, <code>si::newton</code>, and <code>si::watt</code> are examples of named derived quantities within SI.</p> <p><code>non_si::minute</code> is an example of a scaled unit of time.</p> <p><code>si::si2019::speed_of_light_in_vacuum</code> is a physical constant standardized by the SI in 2019.</p> <p><code>Unit</code> can be defined by the user in one of the following ways:</p> <pre><code>template&lt;PrefixableUnit auto U&gt; struct kilo_ : prefixed_unit&lt;\"k\", mag_power&lt;10, 3&gt;, U&gt; {};\ntemplate&lt;PrefixableUnit auto U&gt; inline constexpr kilo_&lt;U&gt; kilo;\ninline constexpr struct second : named_unit&lt;\"s\", kind_of&lt;isq::time&gt;&gt; {} second;\ninline constexpr struct gram : named_unit&lt;\"g\", kind_of&lt;isq::mass&gt;&gt; {} gram;\ninline constexpr struct minute : named_unit&lt;\"min\", mag&lt;60&gt; * second&gt; {} minute;\ninline constexpr struct kilogram : decltype(kilo&lt;gram&gt;) {} kilogram;\ninline constexpr struct newton : named_unit&lt;\"N\", kilogram * metre / square(second)&gt; {} newton;\ninline constexpr struct speed_of_light_in_vacuum : named_unit&lt;\"c\", mag&lt;299'792'458&gt; * metre / second&gt; {} speed_of_light_in_vacuum;\n</code></pre> <p>The unit equation of <code>si::metre / si::second</code> results in the <code>derived_unit&lt;si::metre, per&lt;si::second&gt;&gt;</code> type.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#AssociatedUnit","title":"<code>AssociatedUnit&lt;T&gt;</code>","text":"<p><code>AssociatedUnit</code> concept describes a unit with an associated quantity and is satisfied by:</p> <ul> <li>All units derived from a <code>named_unit</code> class template instantiated with a unique symbol identifier and a <code>QuantitySpec</code>.</li> <li>All units being a result of a unit equations on other associated units.</li> </ul> Examples <p>All units in the SI have associated quantities. For example, <code>si::second</code> is specified to measure <code>isq::time</code>.</p> <p>Natural units typically do not have an associated quantity. For example, if we assume <code>c = 1</code>, a <code>natural::second</code> unit can be used to measure both <code>time</code> and <code>length</code>. In such case <code>speed</code> would be a dimensionless quantity.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#PrefixableUnit","title":"<code>PrefixableUnit&lt;T&gt;</code>","text":"<p><code>PrefixableUnit</code> concept is satisfied by all units derived from a <code>named_unit</code> class template for which a customization point <code>unit_can_be_prefixed&lt;T{}&gt;</code> was not explicitly set to <code>false</code>. Such units can be passed as an argument to a <code>prefixed_unit</code> class template.</p> Examples <p>All units in the SI can be prefixed with SI-defined prefixes.</p> <p>Some off-system units like <code>non_si::day</code> can't be prefixed. To enforce that the following has to be provided:</p> <pre><code>template&lt;&gt; inline constexpr bool unit_can_be_prefixed&lt;non_si::day&gt; = false;\n</code></pre>"},{"location":"users_guide/framework_basics/basic_concepts/#UnitOf","title":"<code>UnitOf&lt;T, V&gt;</code>","text":"<p><code>UnitOf</code> concept is satisfied for all units <code>T</code> matching an <code>AssociatedUnit</code> concept with an associated quantity type implicitly convertible to <code>V</code>.</p> More details <p>Additionally, the kind of <code>V</code> and the kind of quantity type associated with <code>T</code> must be the same, or the quantity type associated with <code>T</code> may not be derived from the kind of <code>V</code>.</p> <p>This condition is required to make <code>dimensionless[si::radian]</code> invalid as <code>si::radian</code> should be only used for <code>isq::angular_measure</code> which is a nested quantity kind within the dimensionless quantities tree.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#Reference","title":"<code>Reference&lt;T&gt;</code>","text":"<p><code>Reference</code> concept is satisfied by all quantity reference types. Such types provide all the meta-information required to create a <code>Quantity</code>. A <code>Reference</code> can either be:</p> <ul> <li>An AssociatedUnit.</li> <li>The instantiation of a <code>reference</code> class template with a <code>QuantitySpec</code> passed as the first template argument and a <code>Unit</code> passed as the second one.</li> </ul> Examples <p><code>si::metre</code> is defined in the SI as a unit of <code>isq::length</code> and thus can be used as a reference to instantiate a quantity of length.</p> <p>The expression <code>isq::height[m]</code> results with <code>reference&lt;isq::height, si::metre&gt;</code> which can be used to instantiate a quantity of <code>isq::height</code> with a unit of <code>si::metre</code>.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#ReferenceOf","title":"<code>ReferenceOf&lt;T, V&gt;</code>","text":"<p><code>ReferenceOf</code> concept is satisfied by references <code>T</code> that match the following value <code>V</code>:</p> <code>V</code> Condition <code>Dimension</code> The dimension of a quantity specification satisfies <code>DimensionOf&lt;V&gt;</code> concept. <code>QuantitySpec</code> The quantity specification satisfies <code>QuantitySpecOf&lt;V&gt;</code> concept. <code>quantity_character</code> The quantity specification has a character of <code>V</code>."},{"location":"users_guide/framework_basics/basic_concepts/#Representation","title":"<code>Representation&lt;T&gt;</code>","text":"<p><code>Representation</code> concept constraints a type of a number that stores the value of a quantity.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#RepresentationOf","title":"<code>RepresentationOf&lt;T, Ch&gt;</code>","text":"<p><code>RepresentationOf</code> concept is satisfied by all <code>Representation</code> types that are of a specified quantity character <code>Ch</code>.</p> <p>A user can declare a custom representation type to be of a specific character by providing the specialization with <code>true</code> for one or more of the following variable templates:</p> <ul> <li><code>is_scalar&lt;T&gt;</code></li> <li><code>is_vector&lt;T&gt;</code></li> <li><code>is_tensor&lt;T&gt;</code></li> </ul> Examples <p>If we want to use scalar types to express vector quantities (e.g. ignoring the \"direction\" of the vector) the following definition can be provided to enable such a behavior:</p> <pre><code>template&lt;class T&gt;\nrequires mp_units::is_scalar&lt;T&gt;\ninline constexpr bool mp_units::is_vector&lt;T&gt; = true;\n</code></pre>"},{"location":"users_guide/framework_basics/basic_concepts/#Quantity","title":"<code>Quantity&lt;T&gt;</code>","text":"<p><code>Quantity</code> concept matches every quantity in the library and is satisfied by all types being or deriving from and instantiation of a <code>quantity</code> class template.</p> Examples <p>All of <code>42 * m</code>, <code>42 * si::metre</code>, <code>42 * isq::height[m]</code>, and <code>isq::height(42 * m)</code> create a quantity and thus satisfy a <code>Quantity</code> concept.</p> <p>A quantity type can also be specified explicitly (i.e. <code>quantity&lt;si::metre, int&gt;</code>, <code>quantity&lt;isq::height[m]&gt;</code>).</p>"},{"location":"users_guide/framework_basics/basic_concepts/#QuantityOf","title":"<code>QuantityOf&lt;T, V&gt;</code>","text":"<p><code>QuantityOf</code> concept is satisfied by all the quantities for which a <code>ReferenceOf&lt;V&gt;</code> is <code>true</code>.</p>"},{"location":"users_guide/framework_basics/basic_concepts/#PointOrigin","title":"<code>PointOrigin&lt;T&gt;</code>","text":"<p><code>PointOrigin</code> concept matches all quantity point origins in the library. It is satisfied by either:</p> <ul> <li>All types derived from an <code>absolute_point_origin</code> class template.</li> <li>All types derived from an <code>relative_point_origin</code> class template.</li> </ul> Examples <p>The types of both definitions below satisfy a <code>PointOrigin</code> concept:</p> <pre><code>inline constexpr struct absolute_zero : absolute_point_origin&lt;isq::thermodynamic_temperature&gt; {} absolute_zero;\ninline constexpr struct ice_point : relative_point_origin&lt;absolute_zero + 273.15 * kelvin&gt; {} ice_point;\n</code></pre>"},{"location":"users_guide/framework_basics/basic_concepts/#PointOriginFor","title":"<code>PointOriginFor&lt;T, V&gt;</code>","text":"<p><code>PointOriginFor</code> concept is satisfied by all <code>PointOrigin</code> types that have quantity type implicitly convertible from quantity specification <code>V</code>, which means that <code>V</code> must satisfy <code>QuantitySpecOf&lt;T::quantity_spec&gt;</code>.</p> Examples <p><code>ice_point</code> can serve as a point origin for points of <code>isq::Celsius_temperature</code> because this quantity type implicitly converts to <code>isq::thermodynamic_temperature</code>.</p> <p>However, if we define <code>mean_sea_level</code> in the following way:</p> <pre><code>inline constexpr struct mean_sea_level : absolute_point_origin&lt;isq::altitude&gt; {} mean_sea_level;\n</code></pre> <p>then it can't be used as a point origin for points of <code>isq::length</code> or <code>isq::width</code> as none of them is implicitly convertible to <code>isq::altitude</code>:</p> <ul> <li>not every \"length\" is an \"altitude\",</li> <li>\"width\" is not compatible with \"altitude\".</li> </ul>"},{"location":"users_guide/framework_basics/basic_concepts/#QuantityPoint","title":"<code>QuantityPoint&lt;T&gt;</code>","text":"<p><code>QuantityPoint</code> concept is satisfied by all types being either a specialization or derived from <code>quantity_point</code> class template.</p> Examples <p>The following specifies a quantity point defined in terms of an <code>ice_point</code> quantity point origin provided in the previous example:</p> <pre><code>constexpr auto room_reference_temperature = ice_point + isq::Celsius_temperature(21 * deg_C);\n</code></pre>"},{"location":"users_guide/framework_basics/basic_concepts/#QuantityPointOf","title":"<code>QuantityPointOf&lt;T, V&gt;</code>","text":"<p><code>QuantityPointOf</code> concept is satisfied by all the quantity points <code>T</code> that match the following value <code>V</code>:</p> <code>V</code> Condition <code>Reference</code> The quantity point reference satisfies <code>ReferenceOf&lt;V&gt;</code> concept. <code>PointOrigin</code> The point and <code>V</code> have the same absolute point origin."},{"location":"users_guide/framework_basics/basic_concepts/#QuantityLike","title":"<code>QuantityLike&lt;T&gt;</code>","text":"<p><code>QuantityLike</code> concept provides interoperability with other libraries and is satisfied by a type <code>T</code> for which an instantiation of <code>quantity_like_traits</code> type trait yields a valid type that provides:</p> <ul> <li>Static data member <code>reference</code> that matches the <code>Reference</code> concept,</li> <li><code>rep</code> type that matches <code>RepresentationOf</code> concept with the character provided in <code>reference</code>,</li> <li><code>value(T)</code> static member function returning a raw value of the quantity.</li> </ul> Examples <p>This is how support for <code>std::chrono::seconds</code> can be provided:</p> <pre><code>template&lt;&gt;\nstruct mp_units::quantity_like_traits&lt;std::chrono::seconds&gt; {\nstatic constexpr auto reference = si::second;\nusing rep = std::chrono::seconds::rep;\n[[nodiscard]] static constexpr rep value(const std::chrono::seconds&amp; q) { return q.count(); }\n};\nquantity q(42s);\n</code></pre>"},{"location":"users_guide/framework_basics/basic_concepts/#QuantityPointLike","title":"<code>QuantityPointLike&lt;T&gt;</code>","text":"<p><code>QuantityPointLike</code> concept provides interoperability with other libraries and is satisfied by a type <code>T</code> for which an instantiation of <code>quantity_point_like_traits</code> type trait yields a valid type that provides:</p> <ul> <li>Static data member <code>reference</code> that matches the <code>Reference</code> concept</li> <li>Static data member <code>point_origin</code> that matches the <code>PointOrigin</code> concept</li> <li><code>rep</code> type that matches <code>RepresentationOf</code> concept with the character provided in <code>reference</code></li> <li><code>quantity_from_origin(T)</code> static member function returning the <code>quantity</code> being the offset of the point from the origin</li> </ul> Examples <p>This is how support for a <code>std::chrono::time_point</code> of <code>std::chrono::seconds</code> can be provided:</p> <pre><code>template&lt;typename C&gt;\nstruct mp_units::quantity_point_like_traits&lt;std::chrono::time_point&lt;C, std::chrono::seconds&gt;&gt; {\nstatic constexpr auto reference = si::second;\nstatic constexpr auto point_origin = chrono_point_origin;\nusing rep = std::chrono::seconds::rep;\n[[nodiscard]] static constexpr auto quantity_from_origin(const std::chrono::time_point&lt;C, std::chrono::seconds&gt;&amp; qp)\n{\nreturn quantity{std::chrono::duration_cast&lt;std::chrono::seconds&gt;(qp.time_since_epoch())};\n}\n};\nquantity_point qp(time_point_cast&lt;std::chrono::seconds&gt;(std::chrono::system_clock::now()));\n</code></pre>"},{"location":"users_guide/framework_basics/character_of_a_quantity/","title":"Character of a Quantity","text":"<p>Warning</p> <p>This chapter's features are experimental and subject to change or removal. Please share your feedback if something seems wrong or could be improved.</p>"},{"location":"users_guide/framework_basics/character_of_a_quantity/#scalars-vectors-and-tensors","title":"Scalars, vectors, and tensors","text":"<p>ISO 80000-2</p> <p>Scalars, vectors and tensors are mathematical objects that can be used to denote certain physical quantities and their values. They are as such independent of the particular choice of a coordinate system, whereas each scalar component of a vector or a tensor and each component vector and component tensor depend on that choice.</p> <p>Such distinction is important because each quantity character represents different properties and allows different operations to be done on its quantities.</p> <p>For example, imagine a physical units library that allows the creation of a <code>speed</code> quantity from both <code>length / time</code> and <code>length * time</code>. It wouldn't be too safe to use such a product, right?</p> <p>Now we have to realize that both of the above operations (multiplication and division) are not even mathematically defined for linear algebra types such as vectors or tensors. On the other hand, two vectors can be passed as arguments to dot and cross-product operations. The result of the first one is a scalar. The second one results in a vector that is perpendicular to both vectors passed as arguments. Again, it wouldn't be safe to allow replacing those two operations with each other or expect the same results from both cases. This simply can't work.</p>"},{"location":"users_guide/framework_basics/character_of_a_quantity/#isq-defines-quantities-of-all-characters","title":"ISQ defines quantities of all characters","text":"<p>While defining quantities ISO 80000 explicitly mentions when a specific quantity has a vector or tensor character. Here are some examples:</p> Quantity Character Quantity Equation <code>duration</code> scalar {base quantity} <code>mass</code> scalar {base quantity} <code>length</code> scalar {base quantity} <code>path_length</code> scalar {base quantity} <code>radius</code> scalar {base quantity} <code>position_vector</code> vector {base quantity} <code>velocity</code> vector <code>position_vector / duration</code> <code>acceleration</code> vector <code>velocity / duration</code> <code>force</code> vector <code>mass * acceleration</code> <code>power</code> scalar <code>force \u22c5 velocity</code> <code>moment_of_force</code> vector <code>position_vector \u00d7 force</code> <code>torque</code> scalar <code>moment_of_force \u22c5 {unit-vector}</code> <code>surface_tension</code> scalar <code>|force| / length</code> <code>angular_displacement</code> scalar <code>path_length / radius</code> <code>angular_velocity</code> vector <code>angular_displacement / duration * {unit-vector}</code> <code>momentum</code> vector <code>mass * velocity</code> <code>angular_momentum</code> vector <code>position_vector \u00d7 momentum</code> <code>moment_of_inertia</code> tensor <code>angular_momentum \u2297 angular_velocity</code> <p>In the above equations:</p> <ul> <li><code>a * b</code> - regular multiplication where one of the arguments has to be scalar</li> <li><code>a / b</code> - regular division where the divisor has to be scalar</li> <li><code>a \u22c5 b</code> - dot product of two vectors</li> <li><code>a \u00d7 b</code> - cross product of two vectors</li> <li><code>|a|</code> - magnitude of a vector</li> <li><code>{unit-vector}</code> - a special vector with the magnitude of <code>1</code></li> <li><code>a \u2297 b</code> - tensor product of two vectors or tensors</li> </ul> <p>Note</p> <p>As of now, all of the C++ physical units libraries on the market besides mp-units do not support the operations mentioned above. They expose only multiplication and division operators, which do not work for linear algebra-based representation types. If a user of those libraries would like to create the quantities provided in the above table properly, this would result in a compile-time error stating that multiplication and division of two linear algebra vectors is impossible.</p>"},{"location":"users_guide/framework_basics/character_of_a_quantity/#characters-dont-apply-to-dimensions-and-units","title":"Characters don't apply to dimensions and units","text":"<p>ISO 80000 explicitly states that dimensions are orthogonal to quantity characters:</p> <p>ISO 80000-1:2009</p> <p>In deriving the dimension of a quantity, no account is taken of its scalar, vector, or tensor character.</p> <p>Also, it explicitly states that:</p> <p>ISO 80000-2</p> <p>All units are scalars.</p>"},{"location":"users_guide/framework_basics/character_of_a_quantity/#defining-vector-and-tensor-quantities","title":"Defining vector and tensor quantities","text":"<p>To specify that a specific quantity has a vector or tensor character a value of <code>quantity_character</code> enumeration can be appended to the <code>quantity_spec</code> describing such a quantity type:</p> C++23C++20Portable <pre><code>inline constexpr struct position_vector : quantity_spec&lt;length, quantity_character::vector&gt; {} position_vector;\ninline constexpr struct displacement : quantity_spec&lt;length, quantity_character::vector&gt; {} displacement;\n</code></pre> <pre><code>inline constexpr struct position_vector : quantity_spec&lt;position_vector, length, quantity_character::vector&gt; {} position_vector;\ninline constexpr struct displacement : quantity_spec&lt;displacement, length, quantity_character::vector&gt; {} displacement;\n</code></pre> <pre><code>QUANTITY_SPEC(position_vector, length, quantity_character::vector);\nQUANTITY_SPEC(displacement, length, quantity_character::vector);\n</code></pre> <p>With the above, all the quantities derived from <code>position_vector</code> or <code>displacement</code> will have a correct character determined according to the kind of operations included in the quantity equation defining a derived quantity.</p> <p>For example, <code>velocity</code> in the below definition will be defined as a vector quantity (no explicit character override is needed):</p> C++23C++20Portable <pre><code>inline constexpr struct velocity : quantity_spec&lt;speed, position_vector / duration&gt; {} velocity;\n</code></pre> <pre><code>inline constexpr struct velocity : quantity_spec&lt;velocity, speed, position_vector / duration&gt; {} velocity;\n</code></pre> <pre><code>QUANTITY_SPEC(velocity, speed, position_vector / duration);\n</code></pre>"},{"location":"users_guide/framework_basics/character_of_a_quantity/#representation-types-for-vector-and-tensor-quantities","title":"Representation types for vector and tensor quantities","text":"<p>As we remember, the <code>quantity</code> class template is defined as follows:</p> <pre><code>template&lt;Reference auto R,\nRepresentationOf&lt;get_quantity_spec(R).character&gt; Rep = double&gt;\nclass quantity;\n</code></pre> <p>The second template parameter is constrained with a <code>RepresentationOf</code> concept that checks if the provided representation type satisfies the requirements for the character associated with this quantity type.</p> <p>Note</p> <p>The current version of the C++ Standard Library does not provide any types that could be used as a representation type for vector and tensor quantities. This is why users are on their own here .</p> <p>To provide examples and implement unit tests, our library uses the types proposed in the P1385 and available as a Conan package in the Conan Center. However, thanks to the provided customization points, any linear algebra library types can be used as a vector or tensor quantity representation type.</p> <p>To enable the usage of a user-defined type as a representation type for vector or tensor quantities, you need to provide a partial specialization of <code>is_vector</code> or <code>is_tensor</code> customization points.</p> <p>For example, here is how it can be done for the P1385 types:</p> <pre><code>#include &lt;matrix&gt;\nusing la_vector = STD_LA::fixed_size_column_vector&lt;double, 3&gt;;\ntemplate&lt;&gt;\ninline constexpr bool mp_units::is_vector&lt;la_vector&gt; = true;\n</code></pre> <p>With the above, we can use <code>la_vector</code> as a representation type for our quantity:</p> <pre><code>Quantity auto q = la_vector{1, 2, 3} * isq::velocity[m / s];\n</code></pre> <p>In case there is an ambiguity of <code>operator*</code> between mp-units and a linear algebra library, we can either:</p> <ul> <li> <p>use <code>make_quantity</code> factory function</p> <pre><code>Quantity auto q = make_quantity&lt;isq::velocity[m / s]&gt;(la_vector{1, 2, 3});\n</code></pre> </li> <li> <p>provide a dedicated overload of <code>operator*</code> that will resolve the ambiguity and wrap the above</p> <pre><code>template&lt;Reference R&gt;\nQuantity auto operator*(la_vector rep, R)\n{\nreturn make_quantity&lt;R{}&gt;(rep);\n}\n</code></pre> </li> </ul> <p>Note</p> <p>The following does not work:</p> <pre><code>Quantity auto q1 = la_vector{1, 2, 3} * (m / s);\nQuantity auto q2 = isq::velocity(la_vector{1, 2, 3} * (m / s));\nquantity&lt;isq::velocity[m/s]&gt; q3{la_vector{1, 2, 3} * (m / s)};\n</code></pre> <p>In all the cases above, the SI unit <code>m / s</code> has an associated scalar quantity of <code>isq::length / isq::time</code>. <code>la_vector</code> is not a correct representation type for a scalar quantity so the construction fails.</p>"},{"location":"users_guide/framework_basics/character_of_a_quantity/#hacking-the-character","title":"Hacking the character","text":"<p>Sometimes you want to use a vector quantity, but you don't care about its direction. For example, the standard gravity acceleration constant always points down, so you might not care about this in a particular scenario. In such a case, you may want to \"hack\" the library to allow scalar types to be used as a representation type for scalar quantities.</p> <p>For example, you can do the following:</p> <pre><code>template&lt;class T&gt;\nrequires mp_units::is_scalar&lt;T&gt;\ninline constexpr bool mp_units::is_vector&lt;T&gt; = true;\n</code></pre> <p>which says that every type that can be used as a scalar representation is also allowed for vector quantities.</p> <p>Doing the above is actually not such a big \"hack\" as the ISO 80000 explicitly allows it:</p> <p>ISO 80000-2</p> <p>A vector is a tensor of the first order and a scalar is a tensor of order zero.</p> <p>Despite it being allowed by ISO 80000, for type-safety reasons, we do not allow such a behavior by default, and a user has to opt into such scenarios explicitly.</p>"},{"location":"users_guide/framework_basics/dimensionless_quantities/","title":"Dimensionless Quantities","text":"<p>The quantities we discussed so far always had some specific type and physical dimension. However, this is not always the case. While performing various computations, we sometimes end up with so-called \"dimensionless\" quantities, which ISO correctly defines as quantities of dimension one:</p> <p>ISO/IEC Guide 99</p> <ul> <li>Quantity for which all the exponents of the factors corresponding to the base quantities in its quantity dimension are zero.</li> <li>The measurement units and values of quantities of dimension one are numbers, but such quantities convey more information than a number.</li> <li>Some quantities of dimension one are defined as the ratios of two quantities of the same kind.</li> <li>Numbers of entities are quantities of dimension one.</li> </ul>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-two-quantities-of-the-same-kind","title":"Dividing two quantities of the same kind","text":"<p>Dividing two quantities of the same kind always results in a quantity of dimension one. However, depending on what type of quantities we divide or what their units are, we may end up with slightly different results.</p> <p>Note</p> <p>In mp-units, dividing two quantities of the same dimension always results in a quantity with the dimension being <code>dimension_one</code>. This is often different for other physical units libraries, which may return a raw representation type for such cases. A raw value is also always returned from the division of two <code>std::chrono::duration</code> objects.</p> <p>To read more about the reasoning for this design decision, please check our FAQ.</p>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-the-same-type","title":"Dividing quantities of the same type","text":"<p>First, let's analyze what happens if we divide two quantities of the same type:</p> <pre><code>constexpr QuantityOf&lt;dimensionless&gt; auto q = isq::height(200 * m) / isq::height(50 * m);\n</code></pre> <p>In such a case, we end up with a dimensionless quantity that has the following properties:</p> <pre><code>static_assert(q.quantity_spec == dimensionless);\nstatic_assert(q.dimension == dimension_one);\nstatic_assert(q.unit == one);\n</code></pre> <p>In case we would like to print its value, we would see a raw value of <code>4</code> in the output with no unit being printed.</p>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-different-types","title":"Dividing quantities of different types","text":"<p>Now let's see what happens if we divide quantities of the same dimension and unit but which have different quantity types:</p> <pre><code>constexpr QuantityOf&lt;dimensionless&gt; auto q = isq::work(200 * J) / isq::heat(50 * J);\n</code></pre> <p>Again we end up with <code>dimension_one</code> and <code>one</code>, but this time:</p> <pre><code>static_assert(q.quantity_spec == isq::work / isq::heat);\n</code></pre> <p>As shown above, the result is not of a <code>dimensionless</code> type anymore. Instead, we get a quantity type derived from the performed quantity equation. According to the ISQ, work divided by heat is the recipe for the thermodynamic efficiency quantity, thus:</p> <pre><code>static_assert(implicitly_convertible(q.quantity_spec, isq::efficiency_thermodynamics));\n</code></pre> <p>Note</p> <p>The quantity of <code>isq::efficiency_thermodynamics</code> is of a kind <code>dimensionless</code>, so it is implicitly convertible to <code>dimensionless</code> and satisfies the <code>QuantityOf&lt;dimensionless&gt;</code> concept.</p>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#dividing-quantities-of-different-units","title":"Dividing quantities of different units","text":"<p>Now, let's see what happens when we divide two quantities of the same type but different units:</p> <pre><code>constexpr QuantityOf&lt;dimensionless&gt; auto q = isq::height(4 * km) / isq::height(2 * m);\n</code></pre> <p>This time we still get a quantity of <code>dimensionless</code> type with a <code>dimension_one</code> as its dimension. However, the resulting unit is not <code>one</code> anymore:</p> <pre><code>static_assert(q.unit == mag_power&lt;10, 3&gt; * one);\n</code></pre> <p>In case we would print the text output of this quantity, we would not see a raw value of <code>2000</code>, but <code>2 km/m</code>.</p> <p>First, it may look surprising, but this is actually consistent with the division of quantities of different dimensions. For example, if we divide <code>4 * km / 2 * s</code>, we do not expect <code>km</code> to be \"expanded\" to <code>m</code> before the division, right? We would expect the result of <code>2 km/s</code>, which is exactly what we get when we divide quantities of the same kind.</p> <p>This is a compelling feature that allows us to express huge or tiny ratios without the need for big and expensive representation types. With this, we can easily define things like a Hubble's constant that uses a unit that is proportional to the ratio of kilometers per megaparsecs, which are both units of length:</p> <pre><code>inline constexpr struct hubble_constant :\nnamed_unit&lt;basic_symbol_text{\"H\u2080\", \"H_0\"}, mag&lt;ratio{701, 10}&gt; * si::kilo&lt;si::metre&gt; / si::second / si::mega&lt;parsec&gt;&gt; {} hubble_constant;\n</code></pre>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#counts-of-things","title":"Counts of things","text":"<p>Another important use case for dimensionless quantities is to provide strong types for counts of things. For example:</p> <ul> <li>ISO-80000-3 provides a <code>rotation</code> quantity defined as the number of revolutions,</li> <li>IEC-80000-6 provides a <code>number_of_turns_in_a_winding</code> quantity,</li> <li>IEC-80000-13 provides a <code>Hamming_distance</code> quantity defined as the number of digit positions in which the corresponding digits of two words of the same length are different.</li> </ul> <p>Thanks to assigning strong names to such quantities, later on they can be explicitly used as arguments in the quantity equations of other quantities deriving from them.</p>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#predefined-units-of-the-dimensionless-quantity","title":"Predefined units of the dimensionless quantity","text":"<p>As we observed above, the most common unit for dimensionless quantities is <code>one</code>. It has the ratio of <code>1</code> and does not output any textual symbol.</p> <p>Important</p> <p>A unit <code>one</code> is special in the entire type system of units as it is considered to be an identity operand in the unit expression templates. This means that, for example:</p> <pre><code>static_assert(one * one == one);\nstatic_assert(one * si::metre == si::metre);\nstatic_assert(si::metre / si::metre == one);\n</code></pre> <p>The same is also true for <code>dimension_one</code> and <code>dimensionless</code> in the domains of dimensions and quantity specifications.</p> <p>Besides the unit <code>one</code>, there are a few other scaled units predefined in the library for usage with dimensionless quantities:</p> <pre><code>inline constexpr struct percent : named_unit&lt;\"%\", mag&lt;ratio{1, 100}&gt; * one&gt; {} percent;\ninline constexpr struct per_mille : named_unit&lt;basic_symbol_text{\"\u2030\", \"%o\"}, mag&lt;ratio(1, 1000)&gt; * one&gt; {} per_mille;\n</code></pre>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#angular-quantities","title":"Angular quantities","text":"<p>Special, often controversial, examples of dimensionless quantities are an angular measure and solid angular measure quantities that are defined in the ISQ to be the result of a division of <code>arc_length / radius</code> and <code>area / pow&lt;2&gt;(radius)</code> respectively. Moreover, ISQ also explicitly states that both can be expressed in the unit <code>one</code>. This means that both <code>isq::angular_measure</code> and <code>isq::solid_angular_measure</code> should be of a kind of <code>dimensionless</code>.</p> <p>On the other hand, ISQ also specifies that a unit <code>radian</code> can be used for <code>isq::angular_measure</code>, and a unit <code>steradian</code> can be used for <code>isq::solid_angular_measure</code>. Those should not be mixed or used to express other types of dimensionless quantities. This means that both <code>isq::angular_measure</code> and <code>isq::solid_angular_measure</code> should also be quantity kinds by themselves.</p> <p>Note</p> <p>Many people claim that angle being a dimensionless quantity is a bad idea. There are proposals submitted to make an angle a base quantity and <code>rad</code> to become a base unit. More on this topic can be found in the \"Strong Angular System\" chapter.</p>"},{"location":"users_guide/framework_basics/dimensionless_quantities/#nested-quantity-kinds","title":"Nested quantity kinds","text":"<p>Angular quantities are not the only ones with such a \"strange\" behavior. Another, but a similar case is a <code>storage_capacity</code> quantity specified in IEC-80000-13 that again allows expressing it in both <code>one</code> and <code>bit</code> units.</p> <p>Those cases make dimensionless quantities an exceptional tree in the library. This is the only quantity hierarchy that contains more than one quantity kind in its tree:</p> <pre><code>flowchart TD\n dimensionless[\"dimensionless\\n[one]\"]\n dimensionless --- rotation\n dimensionless --- efficiency\n dimensionless --- angular_measure[\"angular_measure\\n[rad]\"]\n angular_measure --- rotational_displacement\n angular_measure --- phase_angle\n dimensionless --- solid_angular_measure[\"solid_angular_measure\\n[sr]\"]\n dimensionless --- drag_factor\n dimensionless --- storage_capacity[\"storage_capacity\\n[bit]\"] --- equivalent_binary_storage_capacity\n dimensionless --- ...</code></pre> <p>To provide such support in the library, we provided an <code>is_kind</code> specifier that can be appended to the quantity specification:</p> C++23C++20Portable <pre><code>inline constexpr struct angular_measure : quantity_spec&lt;dimensionless, arc_length / radius, is_kind&gt; {} angular_measure;\ninline constexpr struct solid_angular_measure : quantity_spec&lt;dimensionless, area / pow&lt;2&gt;(radius), is_kind&gt; {} solid_angular_measure;\ninline constexpr struct storage_capacity : quantity_spec&lt;dimensionless, is_kind&gt; {} storage_capacity;\n</code></pre> <pre><code>inline constexpr struct angular_measure : quantity_spec&lt;angular_measure, dimensionless, arc_length / radius, is_kind&gt; {} angular_measure;\ninline constexpr struct solid_angular_measure : quantity_spec&lt;solid_angular_measure, dimensionless, area / pow&lt;2&gt;(radius), is_kind&gt; {} solid_angular_measure;\ninline constexpr struct storage_capacity : quantity_spec&lt;storage_capacity, dimensionless, is_kind&gt; {} storage_capacity;\n</code></pre> <pre><code>QUANTITY_SPEC(angular_measure, dimensionless, arc_length / radius, is_kind);\nQUANTITY_SPEC(solid_angular_measure, dimensionless, area / pow&lt;2&gt;(radius), is_kind);\nQUANTITY_SPEC(storage_capacity, dimensionless, is_kind);\n</code></pre> <p>With the above, we can constrain <code>radian</code>, <code>steradian</code>, and <code>bit</code> to be allowed for usage with specific quantity kinds only:</p> <pre><code>inline constexpr struct radian : named_unit&lt;\"rad\", metre / metre, kind_of&lt;isq::angular_measure&gt;&gt; {} radian;\ninline constexpr struct steradian : named_unit&lt;\"sr\", square(metre) / square(metre), kind_of&lt;isq::solid_angular_measure&gt;&gt; {} steradian;\ninline constexpr struct bit : named_unit&lt;\"bit\", one, kind_of&lt;storage_capacity&gt;&gt; {} bit;\n</code></pre> <p>but still allow a usage of <code>one</code> and its scaled versions for such quantities.</p>"},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/","title":"Faster-than-lightspeed Constants","text":"<p>In most libraries, physical constants are implemented as constant (possibly <code>constexpr</code>) quantity values. Such an approach has some disadvantages, often resulting in longer compilation times and a loss of precision.</p>"},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#simplifying-constants-in-an-equation","title":"Simplifying constants in an equation","text":"<p>When dealing with equations involving physical constants, they often occur more than once in an expression. Such a constant may appear both in a numerator and denominator of a quantity equation. As we know from fundamental physics, we can simplify such an expression by striking a constant out of the equation. Supporting such behavior allows a faster runtime performance and often a better precision of the resulting value.</p>"},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#physical-constants-as-units","title":"Physical constants as units","text":"<p>The mp-units library allows and encourages implementing physical constants as regular units. With that, the constant's value is handled at compile-time, and under favorable circumstances, it can be simplified in the same way as all other repeated units do. If it is not simplified, the value is stored in a type, and the expensive multiplication or division operations can be delayed in time until a user selects a specific unit to represent/print the data.</p> <p>Such a feature often also allows using simpler or faster representation types in the equation. For example, instead of always having to multiply a small integral value with a big floating-point constant number, we can just use the integral type all the way. Only in case a constant will not simplify in the equation, and the user will require a specific unit, such a multiplication will be lazily invoked, and the representation type will need to be expanded to facilitate that. With that, addition, subtractions, multiplications, and divisions will always be the fastest - compiled away or done in out-of-order execution.</p> <p>To benefit from all of the above, in the mp-units library, SI defining and other constants are implemented as units in the following way:</p> <pre><code>namespace si {\nnamespace si2019 {\ninline constexpr struct speed_of_light_in_vacuum :\nnamed_unit&lt;\"c\", mag&lt;299'792'458&gt; * metre / second&gt; {} speed_of_light_in_vacuum;\n} // namespace si2019\ninline constexpr struct magnetic_constant :\nnamed_unit&lt;basic_symbol_text{\"\u03bc\u2080\", \"u_0\"}, mag&lt;4&gt; * mag_pi * mag_power&lt;10, -7&gt; * henry / metre&gt; {} magnetic_constant;\n} // namespace mp_units::si\n</code></pre>"},{"location":"users_guide/framework_basics/faster_than_lightspeed_constants/#usage-examples","title":"Usage examples","text":"<p>With the above definitions, we can calculate vacuum permittivity as:</p> <pre><code>constexpr auto permeability_of_vacuum = 1. * si::magnetic_constant;\nconstexpr auto speed_of_light_in_vacuum = 1 * si::si2019::speed_of_light_in_vacuum;\nQuantityOf&lt;isq::permittivity_of_vacuum&gt; auto q = 1 / (permeability_of_vacuum * pow&lt;2&gt;(speed_of_light_in_vacuum));\nstd::cout &lt;&lt; \"permittivity of vacuum = \" &lt;&lt; q &lt;&lt; \" = \" &lt;&lt; q.in(F / m) &lt;&lt; \"\\n\";\n</code></pre> <p>The above first prints the following:</p> <pre><code>permittivity of vacuum = 1 \u03bc\u2080\u207b\u00b9 c\u207b\u00b2 = 8.85419e-12 F/m\n</code></pre> <p>As we can clearly see, all the calculations above were just about multiplying and dividing the number <code>1</code> with the rest of the information provided as a compile-time type. Only when a user wants a specific SI unit as a result the unit ratios are lazily resolved.</p> <p>Another similar example can be an equation for total energy:</p> <pre><code>QuantityOf&lt;isq::mechanical_energy&gt; auto total_energy(QuantityOf&lt;isq::momentum&gt; auto p,\nQuantityOf&lt;isq::mass&gt; auto m,\nQuantityOf&lt;isq::speed&gt; auto c)\n{\nreturn isq::mechanical_energy(sqrt(pow&lt;2&gt;(p * c) + pow&lt;2&gt;(m * pow&lt;2&gt;(c))));\n}\n</code></pre> <pre><code>constexpr auto GeV = si::giga&lt;si::electronvolt&gt;;\nconstexpr QuantityOf&lt;isq::speed&gt; auto c = 1. * si::si2019::speed_of_light_in_vacuum;\nconstexpr auto c2 = pow&lt;2&gt;(c);\nconst auto p1 = isq::momentum(4. * GeV / c);\nconst QuantityOf&lt;isq::mass&gt; auto m1 = 3. * GeV / c2;\nconst auto E = total_energy(p1, m1, c);\nstd::cout &lt;&lt; \"in `GeV` and `c`:\\n\"\n&lt;&lt; \"p = \" &lt;&lt; p1 &lt;&lt; \"\\n\"\n&lt;&lt; \"m = \" &lt;&lt; m1 &lt;&lt; \"\\n\"\n&lt;&lt; \"E = \" &lt;&lt; E &lt;&lt; \"\\n\";\nconst auto p2 = p1.in(GeV / (m / s));\nconst auto m2 = m1.in(GeV / pow&lt;2&gt;(m / s));\nconst auto E2 = total_energy(p2, m2, c).in(GeV);\nstd::cout &lt;&lt; \"\\nin `GeV`:\\n\"\n&lt;&lt; \"p = \" &lt;&lt; p2 &lt;&lt; \"\\n\"\n&lt;&lt; \"m = \" &lt;&lt; m2 &lt;&lt; \"\\n\"\n&lt;&lt; \"E = \" &lt;&lt; E2 &lt;&lt; \"\\n\";\nconst auto p3 = p1.in(kg * m / s);\nconst auto m3 = m1.in(kg);\nconst auto E3 = total_energy(p3, m3, c).in(J);\nstd::cout &lt;&lt; \"\\nin SI base units:\\n\"\n&lt;&lt; \"p = \" &lt;&lt; p3 &lt;&lt; \"\\n\"\n&lt;&lt; \"m = \" &lt;&lt; m3 &lt;&lt; \"\\n\"\n&lt;&lt; \"E = \" &lt;&lt; E3 &lt;&lt; \"\\n\";\n</code></pre> <p>The above prints the following:</p> <pre><code>in `GeV` and `c`:\np = 4 GeV/c\nm = 3 GeV/c\u00b2\nE = 5 GeV\n\nin `GeV`:\np = 1.33426e-08 GeV s/m\nm = 3.33795e-17 GeV s\u00b2/m\u00b2\nE = 5 GeV\n\nin SI base units:\np = 2.13771e-18 kg m/s\nm = 5.34799e-27 kg\nE = 8.01088e-10 J\n</code></pre>"},{"location":"users_guide/framework_basics/generic_interfaces/","title":"Generic Interfaces","text":"<p>Using a concrete unit in the interface often has a lot of sense. It is especially useful if we store the data internally in the object. In such a case, we have to select a specific unit anyway.</p> <p>For example, let's consider a simple storage tank:</p> <pre><code>class StorageTank {\nquantity&lt;horizontal_area[m2]&gt; base_;\nquantity&lt;isq::height[m]&gt; height_;\nquantity&lt;isq::mass_density[kg / m3]&gt; density_ = air_density;\npublic:\nconstexpr StorageTank(const quantity&lt;horizontal_area[m2]&gt;&amp; base, const quantity&lt;isq::height[m]&gt;&amp; height) :\nbase_(base), height_(height)\n{\n}\n// ...\n};\n</code></pre> <p>As the quantities provided in the function's interface are then stored in the class, there is probably no sense in using generic interfaces here.</p>"},{"location":"users_guide/framework_basics/generic_interfaces/#the-issues-with-unit-specific-interfaces","title":"The issues with unit-specific interfaces","text":"<p>However, in many cases, using a specific unit in the interface is counterproductive. Let's consider the following function:</p> <pre><code>quantity&lt;isq::speed[km / h]&gt; avg_speed(quantity&lt;isq::length[km]&gt; distance,\nquantity&lt;isq::time[h]&gt; duration)\n{\nreturn distance / duration;\n}\n</code></pre> <p>Everything seems fine for now. It also works great if we call it with:</p> <pre><code>quantity&lt;isq::speed[km / h]&gt; s1 = avg_speed(220 * km, 2 * h);\n</code></pre> <p>However, if the user starts doing the following:</p> <pre><code>quantity&lt;isq::speed[mi / h]&gt; s2 = avg_speed(140 * mi, 2 * h);\nquantity&lt;isq::speed[m / s]&gt; s3 = avg_speed(20 * m, 2 * s);\n</code></pre> <p>some issues start to be clearly visible:</p> <ol> <li>The arguments must be converted to units mandated by the function's parameters at each call. This involves potentially expensive multiplication/division operations at runtime.</li> <li>After the function returns the speed in a unit of <code>km/h</code>, another potentially expensive multiplication/division operations have to be performed to convert the resulting quantity into a unit being the derived unit of the initial function's arguments.</li> <li>Besides the obvious runtime cost, some unit conversions may result in a data truncation which means that the result will not be exactly equal to a direct division of the function's arguments.</li> <li> <p>We have to use a floating-point representation type (the <code>quantity</code> class template by default uses <code>double</code> as a representation type) which is considered value preserving. Trying to use an integral type in this scenario will work only for <code>s1</code>, while <code>s2</code> and <code>s3</code> will fail to compile. Failing to compile is a good thing here as the library tries to prevent the user from doing a clearly wrong thing. To make the code compile, the user needs to use dedicated <code>value_cast</code> or <code>force_in</code> like this:</p> <pre><code>quantity&lt;isq::speed[mi / h]&gt; s2 = avg_speed(value_cast&lt;km&gt;(140 * mi), 2 * h);\nquantity&lt;isq::speed[m / s]&gt; s3 = avg_speed((20 * m).force_in(km), (2 * s).force_in(h));\n</code></pre> <p>but the above will obviously provide an incorrect behavior (i.e. division by <code>0</code> in the evaluation of <code>s3</code>).</p> </li> </ol>"},{"location":"users_guide/framework_basics/generic_interfaces/#a-naive-solution","title":"A naive solution","text":"<p>A naive solution here would be to implement the function as an unconstrained function template:</p> <pre><code>auto avg_speed(auto distance, auto duration)\n{\nreturn distance / duration;\n}\n</code></pre> <p>Beware that there are better solutions than this. The above code is too generic. Such a function template accepts everything:</p> <ul> <li>quantities of other types<ul> <li>the compiler will not prevent accidental reordering of the function's arguments</li> <li>quantities of different types can be passed as well</li> </ul> </li> <li>plain <code>double</code> arguments</li> <li><code>std::vector</code> and <code>std::lock_guard</code> will be accepted as well (of course, this will fail in the function's body later in the compilation process)</li> </ul> <p>Note</p> <p>The usage of <code>auto</code> instead of a function parameter type is a C++20 feature. It makes such a code a function template where the type of such a parameter will be deduced during the template instantiation process from the argument type passed by the user.</p>"},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-function-template-arguments-with-concepts","title":"Constraining function template arguments with concepts","text":"<p>Much better generic code can be implemented using basic concepts provided with the library:</p> <pre><code>auto avg_speed(QuantityOf&lt;isq::length&gt; auto distance,\nQuantityOf&lt;isq::time&gt; auto duration)\n{\nreturn isq::speed(distance / duration);\n}\n</code></pre> <p>This explicitly states that the arguments passed by the user must not only satisfy a <code>Quantity</code> concept but also their quantity specification must be implicitly convertible to <code>isq::length</code> and <code>isq::time</code> accordingly. This no longer leaves room for error while still allowing the compiler to generate the most efficient code.</p> <p>Tip</p> <p>Please note that now it is safe just to use integral types all the way which again improves the runtime performance as the multiplication/division operations are often faster on integral rather than floating-point types.</p>"},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-function-template-return-type","title":"Constraining function template return type","text":"<p>The above function template resolves all of the issues described before. However, we can do even better here by additionally constraining the return type:</p> <pre><code>QuantityOf&lt;isq::speed&gt; auto avg_speed(QuantityOf&lt;isq::length&gt; auto distance,\nQuantityOf&lt;isq::time&gt; auto duration)\n{\nreturn isq::speed(distance / duration);\n}\n</code></pre> <p>Doing so has two important benefits:</p> <ol> <li>It informs the users of our interface about what to expect to be the result of a function invocation. It is superior to just returning <code>auto</code>, which does not provide any hint about the thing being returned there.</li> <li>Such a concept constrains the type returned from the function. This means that it works as a unit test to verify if our function actually performs what it is supposed to do. If there is an error in quantity equations, we will learn about it right away.</li> </ol>"},{"location":"users_guide/framework_basics/generic_interfaces/#constraining-a-variable-on-the-stack","title":"Constraining a variable on the stack","text":"<p>If we know exactly what the function does in its internals and if we know the exact argument types passed to such a function, we often know the exact type that will be returned from its invocation.</p> <p>However, if we care about performance, we should often use the generic interfaces described in this chapter. A side effect is that we sometimes are unsure about the return type. Even if we know it today, it might change a week from now due to some code refactoring.</p> <p>In such cases, we can again use <code>auto</code> to denote the type:</p> <pre><code>auto s1 = avg_speed(220 * km, 2 * h);\nauto s2 = avg_speed(140 * mi, 2 * h);\nauto s3 = avg_speed(20 * m, 2 * s);\n</code></pre> <p>In this case, it is probably OK to do so as the <code>avg_speed</code> function name explicitly provides the information on what to expect as a result.</p> <p>In other scenarios where the returned quantity type is not so obvious, it is again helpful to constrain the type with a concept like so:</p> <pre><code>QuantityOf&lt;isq::speed&gt; auto s1 = avg_speed(220 * km, 2 * h);\nQuantityOf&lt;isq::speed&gt; auto s2 = avg_speed(140 * mi, 2 * h);\nQuantityOf&lt;isq::speed&gt; auto s3 = avg_speed(20 * m, 2 * s);\n</code></pre> <p>Again this explicitly provides additional information about the quantity we are dealing with in the code, and it serves as a unit test checking if the \"thing\" returned from a function is actually what we expected here.</p> <p>Note</p> <p>The <code>QuantityOf</code> and <code>QuantityPointOf</code> concepts are probably the most useful, but there are a few more to play with. A list of all the concepts can be found in the \"Basic Concepts\" chapter.</p>"},{"location":"users_guide/framework_basics/interface_introduction/","title":"Interface Introduction","text":""},{"location":"users_guide/framework_basics/interface_introduction/#new-style-of-definitions","title":"New style of definitions","text":"<p>The mp-units library decided to use a rather unusual pattern to define entities. Here is how we define <code>metre</code> and <code>second</code> SI base units:</p> <pre><code>inline constexpr struct metre : named_unit&lt;\"m\", kind_of&lt;isq::length&gt;&gt; {} metre;\ninline constexpr struct second : named_unit&lt;\"s\", kind_of&lt;isq::time&gt;&gt; {} second;\n</code></pre> <p>Please note that the above reuses the same identifier for a type and its object. The rationale behind this is that:</p> <ul> <li>Users always work with objects and never have to spell such a type name.</li> <li>The types appear in the compilation errors and during debugging.</li> </ul> <p>Important</p> <p>To improve compiler errors' readability and make it easier to correlate them with a user's written code, a new idiom in the library is to use the same identifier for a type and its instance.</p>"},{"location":"users_guide/framework_basics/interface_introduction/#strong-types-instead-of-aliases","title":"Strong types instead of aliases","text":"<p>Let's look again at the above units definitions. Another important point to notice is that all the types describing entities in the library are short, nicely named identifiers that derive from longer, more verbose class template instantiations. This is really important to improve the user experience while debugging the program or analyzing the compilation error.</p> <p>Note</p> <p>Such a practice is rare in the industry. Some popular C++ physical units libraries generate enormously long error messages where even only the first line failed o fit on a slide with a tiny font.</p>"},{"location":"users_guide/framework_basics/interface_introduction/#entities-composability","title":"Entities composability","text":"<p>Many physical units libraries (in C++ or any other programming language) assign strong types to library entities (i.e. derived units). While <code>metre_per_second</code> as a type may not look too scary, consider, for example, units of angular momentum. If we followed this path, its coherent unit would look like <code>kilogram_metre_sq_per_second</code>. Now, consider how many scaled versions of this unit would you predefine in the library to ensure that all users are happy with your choice? How expensive would it be from the implementation point of view? What about potential future standardization efforts?</p> <p>This is why in mp-units, we put a strong requirement to make everything as composable as possible. For example, to create a quantity with a unit of speed, one may write:</p> <pre><code>quantity&lt;si::metre / si::second&gt; q;\n</code></pre> <p>In case you use such an unit often and would prefer to have a handy helper for it, you can always do something like this:</p> <pre><code>constexpr auto metre_per_second = si::metre / si::second;\nquantity&lt;metre_per_second&gt; q;\n</code></pre> <p>or choose any shorter identifier of your choice.</p> <p>Coming back to the angular momentum case, thanks to the composability of units, a user can create such a quantity in the following way:</p> <pre><code>using namespace mp_units::si::unit_symbols;\nauto q = la_vector{1, 2, 3} * isq::angular_momentum[kg * m2 / s];\n</code></pre> <p>It is a much better solution. It is terse and easy to understand. Please also notice how easy it is to obtain any scaled version of such a unit (i.e. <code>mg * square(mm) / min</code>) without having to introduce hundreds of types to predefine them.</p>"},{"location":"users_guide/framework_basics/interface_introduction/#value-based-equations","title":"Value-based equations","text":"<p>The mp-units library is based on C++20, which greatly improves a user's experience. One of such improvements are value-based equations.</p> <p>As we have learned above, the entities are being used as values in the code, and they compose. Moreover, derived entities can be defined in the library using such value-based equations. This is a huge improvement compared to what we can find in other physical units libraries or what we have to deal with when we want to write some equations for <code>std::ratio</code>.</p> <p>For example, below are a few definitions of the SI derived units showing the power of C++20 extensions to Non-Type Template Parameters, which allows us to directly pass a result of the value-based unit equation to a class template definition:</p> <pre><code>inline constexpr struct newton : named_unit&lt;\"N\", kilogram * metre / square(second)&gt; {} newton;\ninline constexpr struct pascal : named_unit&lt;\"Pa\", newton / square(metre)&gt; {} pascal;\ninline constexpr struct joule : named_unit&lt;\"J\", newton * metre&gt; {} joule;\n</code></pre>"},{"location":"users_guide/framework_basics/interface_introduction/#expression-templates","title":"Expression templates","text":"<p>The previous chapter provided a rationale for not having predefined types for derived entities. In many libraries, such an approach results in long and unreadable compilation errors, as framework-generated types are typically far from being easy to read and understand.</p> <p>The mp-units library greatly improves the user experience by extensively using expression templates. Such expressions are used consistently throughout the entire library to describe the results of:</p> <ul> <li>dimension equation - the result is put into the <code>derived_dimension&lt;&gt;</code> class template</li> <li>quantity equation - the result is put into the <code>derived_quantity_spec&lt;&gt;</code> class template</li> <li>unit equation - the result is put into the <code>derived_unit&lt;&gt;</code> class template</li> </ul> <p>For example, if we take the above-defined base units and put the results of their division into the quantity class template like this:</p> <pre><code>quantity&lt;metre / second&gt; q;\n</code></pre> <p>we will observe the following type in the debugger</p> <pre><code>(gdb) ptype q\ntype = class mp_units::quantity&lt;mp_units::derived_unit&lt;metre, mp_units::per&lt;second&gt;&gt;(), double&gt; [with Rep = double] {\n</code></pre> <p>The same type identifier will be visible in the compilation error (in case it happens).</p> <p>Important</p> <p>Expressions templates are extensively used throughout the library to improve the readability of the resulting types.</p>"},{"location":"users_guide/framework_basics/interface_introduction/#identities","title":"Identities","text":"<p>As mentioned above, equations can be done on dimensions, quantities, and units. Each such domain must introduce an identity object that can be used in the resulting expressions. Here is the list of identities used in the library:</p> Domain Concept Identity <code>Dimension</code> <code>dimension_one</code> <code>QuantitySpec</code> <code>dimensionless</code> <code>Unit</code> <code>one</code> <p>In the equations, a user can refer to an identity object either explicitly:</p> <pre><code>constexpr auto my_unit = one / second;\n</code></pre> <p>or implicitly:</p> <pre><code>constexpr auto my_unit = 1 / second;\n</code></pre> <p>Both cases with result in the same expression template being generated and put into the wrapper class template.</p>"},{"location":"users_guide/framework_basics/interface_introduction/#supported-operations-and-their-results","title":"Supported operations and their results","text":"<p>There are only a few operations that one can do on such entities and the result of each of them has its unique representation in the library:</p> Operation Resulting template expression arguments <code>A * B</code> <code>A, B</code> <code>B * A</code> <code>A, B</code> <code>A * A</code> <code>power&lt;A, 2&gt;</code> <code>{identity} * A</code> <code>A</code> <code>A * {identity}</code> <code>A</code> <code>A / B</code> <code>A, per&lt;B&gt;</code> <code>A / A</code> <code>{identity}</code> <code>A / {identity}</code> <code>A</code> <code>{identity} / A</code> <code>{identity}, per&lt;A&gt;</code> <code>pow&lt;2&gt;(A)</code> <code>power&lt;A, 2&gt;</code> <code>pow&lt;2&gt;({identity})</code> <code>{identity}</code> <code>sqrt(A)</code> or <code>pow&lt;1, 2&gt;(A)</code> <code>power&lt;A, 1, 2&gt;</code> <code>sqrt({identity})</code> or <code>pow&lt;1, 2&gt;({identity})</code> <code>{identity}</code>"},{"location":"users_guide/framework_basics/interface_introduction/#simplifying-the-resulting-expression-templates","title":"Simplifying the resulting expression templates","text":"<p>To limit the length and improve the readability of generated types, there are many rules to simplify the resulting expression template.</p> <ol> <li> <p>Ordering</p> <p>The resulting comma-separated arguments of multiplication are always sorted according to a specific predicate. This is why:</p> <pre><code>static_assert(A * B == B * A);\nstatic_assert(std::is_same_v&lt;decltype(A * B), decltype(B * A)&gt;);\n</code></pre> <p>This is probably the most important of all steps, as it allows comparing types and enables the rest of simplification rules.</p> </li> <li> <p>Aggregation</p> <p>In case two of the same identifiers are found next to each other on the argument list they will be aggregated in one entry:</p> Before After <code>A, A</code> <code>power&lt;A, 2&gt;</code> <code>A, power&lt;A, 2&gt;</code> <code>power&lt;A, 3&gt;</code> <code>power&lt;A, 1, 2&gt;, power&lt;A, 2&gt;</code> <code>power&lt;A, 5, 2&gt;</code> <code>power&lt;A, 1, 2&gt;, power&lt;A, 1, 2&gt;</code> <code>A</code> </li> <li> <p>Simplification</p> <p>In case two of the same identifiers are found in the numerator and denominator argument lists; they are being simplified into one entry:</p> Before After <code>A, per&lt;A&gt;</code> <code>{identity}</code> <code>power&lt;A, 2&gt;, per&lt;A&gt;</code> <code>A</code> <code>power&lt;A, 3&gt;, per&lt;A&gt;</code> <code>power&lt;A, 2&gt;</code> <code>A, per&lt;power&lt;A, 2&gt;&gt;</code> <code>{identity}, per&lt;A&gt;</code> </li> <li> <p>Repacking</p> <p>In case an expression uses two results of other operations, the components of its arguments are repacked into one resulting type and simplified there.</p> <p>For example, assuming:</p> <pre><code>constexpr auto X = A / B;\n</code></pre> <p>then:</p> Operation Resulting template expression arguments <code>X * B</code> <code>A</code> <code>X * A</code> <code>power&lt;A, 2&gt;, per&lt;B&gt;</code> <code>X * X</code> <code>power&lt;A, 2&gt;, per&lt;power&lt;B, 2&gt;&gt;</code> <code>X / X</code> <code>{identity}</code> <code>X / A</code> <code>{identity}, per&lt;B&gt;</code> <code>X / B</code> <code>A, per&lt;power&lt;B, 2&gt;&gt;</code> </li> </ol>"},{"location":"users_guide/framework_basics/interface_introduction/#example","title":"Example","text":"<p>Thanks to all of the features described above, a user may write the code like this one:</p> <pre><code>using namespace mp_units::si::unit_symbols;\nauto speed = 60. * isq::speed[km / h];\nauto duration = 8 * s;\nauto acceleration = speed / duration;\nstd::cout &lt;&lt; \"acceleration: \" &lt;&lt; acceleration &lt;&lt; \" (\" &lt;&lt; acceleration.in(m / s2) &lt;&lt; \")\\n\";\n</code></pre> <p>The <code>acceleration</code>, being the result of the above code, has the following type (after stripping the <code>mp_units</code> namespace for brevity):</p> <pre><code>quantity&lt;reference&lt;derived_quantity_spec&lt;isq::speed, per&lt;isq::time&gt;&gt;{}, derived_unit&lt;si::kilo_&lt;si::metre{}&gt;, per&lt;non_si::hour, si::second&gt;&gt;{}&gt;{}, int&gt;\n</code></pre> <p>and the text output presents:</p> <pre><code>acceleration: 7.5 km h\u207b\u00b9 s\u207b\u00b9 (2.08333 m/s\u00b2)\n</code></pre>"},{"location":"users_guide/framework_basics/obtaining_metadata/","title":"Obtaining Metadata","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#quantity-spec","title":"quantity spec","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#unit","title":"unit","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#reference","title":"reference","text":""},{"location":"users_guide/framework_basics/obtaining_metadata/#quantity","title":"quantity","text":""},{"location":"users_guide/framework_basics/quantity_arithmetics/","title":"Quantity Arithmetics","text":""},{"location":"users_guide/framework_basics/quantity_arithmetics/#quantity-is-a-numeric-wrapper","title":"<code>quantity</code> is a numeric wrapper","text":"<p>If we think about it, the <code>quantity</code> class template is just a \"smart\" numeric wrapper. It exposes properly constrained set of arithmetic operations on one or two operands.</p> <p>Important</p> <p>Every single arithmetic operator is exposed by the <code>quantity</code> class template only if the underlying representation type provides it as well and its implementation has proper semantics (i.e. returns a reasonable type).</p> <p>For example, in the following code, <code>-a</code> will compile only if <code>MyInt</code> exposes such an operation as well:</p> <pre><code>quantity a = MyInt{42} * m;\nquantity b = -a;\n</code></pre> <p>Assuming that:</p> <ul> <li><code>q</code> is our quantity,</li> <li><code>qq</code> is a quantity implicitly convertible to <code>q</code>,</li> <li><code>q2</code> is any other quantity,</li> <li><code>kind</code> is a quantity of the same kind as <code>q</code>,</li> <li><code>one</code> is a quantity of <code>dimension_one</code> with the unit <code>one</code>,</li> <li><code>number</code> is a value of a type \"compatible\" with <code>q</code>'s representation type,</li> </ul> <p>here is the list of all the supported operators:</p> <ul> <li>unary:<ul> <li><code>+q</code></li> <li><code>-q</code></li> <li><code>++q</code></li> <li><code>q++</code></li> <li><code>--q</code></li> <li><code>q--</code></li> </ul> </li> <li>compound assignment:<ul> <li><code>q += qq</code></li> <li><code>q -= qq</code></li> <li><code>q %= qq</code></li> <li><code>q *= number</code></li> <li><code>q *= one</code></li> <li><code>q /= number</code></li> <li><code>q /= one</code></li> </ul> </li> <li>binary:<ul> <li><code>q + kind</code></li> <li><code>q - kind</code></li> <li><code>q % kind</code></li> <li><code>q * q2</code></li> <li><code>q * number</code></li> <li><code>number * q</code></li> <li><code>q / q2</code></li> <li><code>q / number</code></li> <li><code>number / q</code></li> </ul> </li> <li>ordering and comparison:<ul> <li><code>q == kind</code></li> <li><code>q &lt;=&gt; kind</code></li> </ul> </li> </ul> <p>As we can see, there are plenty of operations one can do on a value of a <code>quantity</code> type. As most of them are obvious, in the following chapters, we will discuss only the most important or non-trivial aspects of quantity arithmetics.</p>"},{"location":"users_guide/framework_basics/quantity_arithmetics/#addition-and-subtraction","title":"Addition and subtraction","text":"<p>Quantities can easily be added or subtracted from each other:</p> <pre><code>static_assert(1 * m + 1 * m == 2 * m);\nstatic_assert(2 * m - 1 * m == 1 * m);\nstatic_assert(isq::height(1 * m) + isq::height(1 * m) == isq::height(2 * m));\nstatic_assert(isq::height(2 * m) - isq::height(1 * m) == isq::height(1 * m));\n</code></pre> <p>The above uses the same types for LHS, RHS, and the result, but in general, we can add, subtract, or compare the values of any quantity type as long as both quantities are of the same kind. The result of such an operation will be the common type of the arguments:</p> <pre><code>static_assert(1 * km + 1.5 * m == 1001.5 * m);\nstatic_assert(isq::height(1 * m) + isq::width(1 * m) == isq::length(2 * m));\nstatic_assert(isq::height(2 * m) - isq::distance(0.5 * m) == 1.5 * m);\nstatic_assert(isq::radius(1 * m) - 0.5 * m == isq::radius(0.5 * m));\n</code></pre> <p>Note</p> <p>Please note that for the compound assignment operators, both arguments have to either be of the same type or the RHS has to be implicitly convertible to the LHS, as the type of LHS is always the result of such an operation:</p> <pre><code>static_assert((1 * m += 1 * km) == 1001 * m);\nstatic_assert((isq::height(1.5 * m) -= 1 * m) == isq::height(0.5 * m));\n</code></pre> <p>If we break those rules, the following code will not compile:</p> <pre><code>static_assert((1 * m -= 0.5 * m) == 0.5 * m); // Compile-time error(1)\nstatic_assert((1 * km += 1 * m) == 1001 * m); // Compile-time error(2)\nstatic_assert((isq::height(1 * m) += isq::length(1 * m)) == 2 * m); // Compile-time error(3)\n</code></pre> <ol> <li>Floating-point to integral representation type is considered narrowing.</li> <li>Conversion of quantity with integral representation type from a unit of a higher resolution to the one with a lower resolution is considered narrowing.</li> <li>Conversion from a more generic quantity type to a more specific one is considered unsafe.</li> </ol>"},{"location":"users_guide/framework_basics/quantity_arithmetics/#multiplication-and-division","title":"Multiplication and division","text":"<p>Multiplying or dividing a quantity by a number does not change its quantity type or unit. However, its representation type may change. For example:</p> <pre><code>static_assert(isq::height(3 * m) * 0.5 == isq::height(1.5 * m));\n</code></pre> <p>Note</p> <p>Unless we use a compound assignment operator, in which case truncating operations are again not allowed:</p> <pre><code>static_assert((isq::height(3 * m) *= 0.5) == isq::height(1.5 * m)); // Compile-time error(1)\n</code></pre> <ol> <li>Floating-point to integral representation type is considered narrowing.</li> </ol> <p>However, suppose we multiply or divide quantities of the same or different types, or we divide a raw number by a quantity. In that case, we most probably will end up in a quantity of yet another type:</p> <pre><code>static_assert(120 * km / (2 * h) == 60 * (km / h));\nstatic_assert(isq::width(2 * m) * isq::length(2 * m) == isq::area(4 * m2));\nstatic_assert(50 / isq::time(1 * s) == isq::frequency(50 * Hz));\n</code></pre> <p>Note</p> <p>An exception from the above rule happens when one of the arguments is a dimensionless quantity. If we multiply or divide by such a quantity, the quantity type will not change. If such a quantity has a unit <code>one</code>, also the unit of a quantity will not change:</p> <pre><code>static_assert(120 * m / (2 * one) == 60 * m);\n</code></pre> <p>An interesting special case happens when we divide the same quantity kinds or multiply a quantity by its inverted type. In such a case, we end up with a dimensionless quantity.</p> <pre><code>static_assert(isq::height(4 * m) / isq::width(2 * m) == 2 * one); // (1)!\nstatic_assert(5 * h / (120 * min) == 0 * one); // (2)!\nstatic_assert(5. * h / (120 * min) == 2.5 * one);\n</code></pre> <ol> <li>The resulting quantity type of the LHS is <code>isq::height / isq::width</code>, which is a quantity of the dimensionless kind.</li> <li>The resulting quantity of the LHS is <code>0 * dimensionless[h / min]</code>. To be consistent with the division of different quantity types, we do not convert quantity values to a common unit before the division.</li> </ol> <p>Beware of integral division</p> <p>The physical units library can't do any runtime branching logic for the division operator. All logic has to be done at compile-time when the actual values are not known, and the quantity types can't change at runtime.</p> <p>If we expect <code>120 * km / (2 * h)</code> to return <code>60 km / h</code>, we have to agree with the fact that <code>5 * km / (24 * h)</code> returns <code>0 km/h</code>. We can't do a range check at runtime to dynamically adjust scales and types based on the values of provided function arguments.</p> <p>This is why we often prefer floating-point representation types when dealing with units. Some popular physical units libraries even forbid integer division at all.</p>"},{"location":"users_guide/framework_basics/quantity_arithmetics/#modulo","title":"Modulo","text":"<p>Now that we know how addition, subtraction, multiplication, and division work, it is time to talk about modulo. What would we expect to be returned from the following quantity equation?</p> <pre><code>auto q = 5 * h % (120 * min);\n</code></pre> <p>Most of us would probably expect to see <code>1 h</code> or <code>60 min</code> as a result. And this is where the problems start.</p> <p>C++ language defines its <code>/</code> and <code>%</code> operators with the quotient-remainder theorem:</p> <pre><code>q = a / b;\nr = a % b;\nq * b + r == a;\n</code></pre> <p>The important property of the modulo operation is that it only works for integral representation types (it is undefined what modulo for floating-point types means). However, as we saw in the previous chapter, integral types are tricky because they often truncate the value.</p> <p>From the quotient-remainder theorem, the result of modulo operation is <code>r = a - q * b</code>. Let's see what we get from such a quantity equation on integral representation types:</p> <pre><code>const quantity a = 5 * h;\nconst quantity b = 120 * min;\nconst quantity q = a / b;\nconst quantity r = a - q * b;\nstd::cout &lt;&lt; \"reminder: \" &lt;&lt; r &lt;&lt; \"\\n\";\n</code></pre> <p>The above code outputs:</p> <pre><code>reminder: 5 h\n</code></pre> <p>And now, a tough question needs an answer. Do we really want modulo operation on physical units to be consistent with the quotient-remainder theorem and return <code>5 h</code> for <code>5 * h % (120 * min)</code>?</p> <p>This is exactly why we decided not to follow this hugely surprising path in the mp-units library. The selected approach was also consistent with the feedback from the C++ experts. For example, this is what Richard Smith said about this issue:</p> <p>Richard Smith</p> <p>I think the quotient-remainder property is a less important motivation here than other factors -- the constraints on <code>%</code> and <code>/</code> are quite different, so they lack the inherent connection they have for integers. In particular, I would expect that <code>A / B</code> works for all quantities <code>A</code> and <code>B</code>, whereas <code>A % B</code> is only meaningful when <code>A</code> and <code>B</code> have the same dimension. It seems like a nice-to-have for the property to apply in the case where both <code>/</code> and <code>%</code> are defined, but internal consistency of <code>/</code> across all cases seems much more important to me.</p> <p>I would expect <code>61 min % 1 h</code> to be <code>1 min</code>, and <code>1 h % 59 min</code> to also be <code>1 min</code>, so my intuition tells me that the result type of <code>A % B</code>, where <code>A</code> and <code>B</code> have the same dimension, should have the smaller unit of <code>A</code> and <code>B</code> (and if the smaller one doesn't divide the larger one, we should either use the <code>gcd / std::common_type</code> of the units of <code>A</code> and <code>B</code> or perhaps just produce an error). I think any other behavior for <code>%</code> is hard to defend.</p> <p>On the other hand, for division it seems to me that the choice of unit should probably not affect the result, and so if we want that <code>5 mm / 120 min = 0 mm/min</code>, then <code>5 h / 120 min == 0 hc</code> (where <code>hc</code> is a dimensionless \"hexaconta\", or <code>60x</code>, unit). I don't like the idea of taking SI base units into account; that seems arbitrary and like it would do the wrong thing as often as it does the right thing, especially when the units have a multiplier that is very large or small. We could special-case the situation of a dimensionless quantity, but that could lead to problematic overflow pretty easily: a calculation such as <code>10 s * 5 GHz * 2 uW</code> would overflow an <code>int</code> if it produces a dimensionless quantity for <code>10 s * 5 GHz</code>, but it could equally produce <code>50 G * 2 uW = 100 kW</code> without any overflow, and presumably would if the terms were merely reordered.</p> <p>If people want to use integer-valued quantities, I think it's fundamental that you need to know what the units of the result of an operation will be, and take that into account in how you express computations; the simplest rule for heterogeneous operators like <code>*</code> or <code>/</code> seems to be that the units of the result are determined by applying the operator to the units of the operands -- and for homogeneous operators like <code>+</code> or <code>%</code>, it seems like the only reasonable option is that you get the <code>std::common_type</code> of the units of the operands.</p> <p>To summarize, the modulo operation on physical units has more in common with addition and division operators than with the quotient-remainder theorem. To avoid surprising results, the operation uses a common unit to do the calculation and provide its result:</p> <pre><code>static_assert(5 * h / (120 * min) == 0 * one);\nstatic_assert(5 * h % (120 * min) == 60 * min);\nstatic_assert(61 * min % (1 * h) == 1 * min);\nstatic_assert(1 * h % (59 * min) == 1 * min);\n</code></pre>"},{"location":"users_guide/framework_basics/quantity_arithmetics/#comparison-against-zero","title":"Comparison against zero","text":"<p>In our code, we often want to compare the value of a quantity against zero. For example, we do it every time when we want to ensure that we deal with a non-zero or positive value.</p> <p>We could implement such checks in the following way:</p> <pre><code>if (q1 / q2 != 0 * (m / s))\n// ...\n</code></pre> <p>The above would work (assuming we are dealing with the quantity of speed), but could be suboptimal if the result of <code>q1 / q2</code> is not expressed in <code>m / s</code>. To eliminate the need for conversion, we need to write:</p> <pre><code>if (auto q = q1 / q2; q != q.zero())\n// ...\n</code></pre> <p>but that is a bit inconvenient, and inexperienced users could be unaware of this technique and its reasons.</p> <p>For the above reasons, the library provides dedicated interfaces to compare against zero that follow the naming convention of named comparison functions in the C++ Standard Library. The mp-units/compare.h header file exposes the following functions:</p> <ul> <li><code>is_eq_zero</code></li> <li><code>is_neq_zero</code></li> <li><code>is_lt_zero</code></li> <li><code>is_gt_zero</code></li> <li><code>is_lteq_zero</code></li> <li><code>is_gteq_zero</code></li> </ul> <p>Thanks to them, to save typing and not pay for unneeded conversions, our check could be implemented as follows:</p> <pre><code>if (is_neq_zero(q1 / q2))\n// ...\n</code></pre> <p>Tip</p> <p>Those functions will work with any type <code>T</code> that exposes <code>zero()</code> member function returning something comparable to <code>T</code>. Thanks to that, we can use them not only with quantities but also with quantity points, <code>std::chrono::duration</code> or any other type that exposes such an interface.</p>"},{"location":"users_guide/framework_basics/quantity_arithmetics/#other-maths","title":"Other maths","text":"<p>This chapter scopes only on the <code>quantity</code> type's operators. However, there are many named math functions provided in the mp-units/math.h header file. Among others, we can find there the following:</p> <ul> <li><code>pow()</code>, <code>sqrt()</code>, and <code>cbrt()</code>,</li> <li><code>exp()</code>,</li> <li><code>abs()</code>,</li> <li><code>epsilon()</code>,</li> <li><code>floor()</code>, <code>ceil()</code>, <code>round()</code>,</li> <li><code>inverse()</code>,</li> <li><code>hypot()</code>,</li> <li><code>sin()</code>, <code>cos()</code>, <code>tan()</code>,</li> <li><code>asin()</code>, <code>acos()</code>, <code>atan()</code>.</li> </ul> <p>In the library, we can also find mp-units/random.h header file with all the pseudo-random number generators.</p>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/","title":"Simple and Typed Quantities","text":"<p>ISO specifies a quantity as:</p> <p>Quote</p> <p>property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference</p> <p>After that, it says:</p> <p>Quote</p> <p>A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such.</p>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#quantity-class-template","title":"<code>quantity</code> class template","text":"<p>In the mp-units library, a quantity is represented with the following class template:</p> <pre><code>template&lt;Reference auto R,\nRepresentationOf&lt;get_quantity_spec(R).character&gt; Rep = double&gt;\nclass quantity;\n</code></pre> <p>The concept <code>Reference</code> is satisfied by either:</p> <ul> <li>a unit with an associated quantity type (i.e. <code>si::metre</code>)</li> <li>a reference type explicitly specifying the quantity type and its unit.</li> </ul> <p>Important</p> <p>All units in the SI system have an associated quantity type.</p> <p>A reference type is implicitly created as a result of the following expression:</p> <pre><code>constexpr auto ref = isq::length[m];\n</code></pre> <p>The above example resulted in the following type <code>reference&lt;isq::length(), si::metre()&gt;</code> being instantiated.</p> <p>Based on this property, the mp-units library provides two modes of dealing with quantities.</p>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#simple-quantities","title":"Simple quantities","text":"<p>The simple mode might be preferred by many developers. It is all about units. Quantities using this mode have shorter type identifiers, resulting in easier-to-understand error messages and better debugging experience.</p> <p>Here is a simple example showing how to deal with such quantities:</p> <pre><code>#include &lt;mp-units/ostream.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;iostream&gt;\nusing namespace mp_units;\nconstexpr quantity&lt;si::metre / si::second&gt; avg_speed(quantity&lt;si::metre&gt; d,\nquantity&lt;si::second&gt; t)\n{\nreturn d / t;\n}\nint main()\n{\nusing namespace mp_units::si::unit_symbols;\nconst quantity distance = 110 * km;\nconst quantity duration = 2 * h;\nconst quantity speed = avg_speed(distance, duration);\nstd::cout &lt;&lt; \"A car driving \" &lt;&lt; distance &lt;&lt; \" in \" &lt;&lt; duration\n&lt;&lt; \" has an average speed of \" &lt;&lt; speed\n&lt;&lt; \" (\" &lt;&lt; speed.in(km / h) &lt;&lt; \")\\n\";\n}\n</code></pre> <p>The code above prints:</p> <pre><code>A car driving 110 km in 2 h has an average speed of 15.2778 m/s (55 km/h)\n</code></pre> <p>Try it on Compiler Explorer</p>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#easy-to-understand-compilation-error-messages","title":"Easy to understand compilation error messages","text":"<p>In case a user makes an error in a quantity equation and the result of the calculation will not match the function return type, the compiler will detect such an issue at compile-time.</p> <p>For example, in case we will make the following error:</p> <pre><code>constexpr quantity&lt;si::metre / si::second&gt; avg_speed(quantity&lt;si::metre&gt; d,\nquantity&lt;si::second&gt; t)\n{\nreturn d * t; // (1)!\n}\n</code></pre> <ol> <li>Quantities multiplied (instead of divided) by accident.</li> </ol> <p>the following compilation error message will be provided:</p> <pre><code>In function 'constexpr mp_units::quantity&lt;mp_units::derived_unit&lt;mp_units::si::metre, mp_units::per&lt;mp_units::si::second&gt; &gt;()&gt; avg_speed(mp_units::quantity&lt;mp_units::si::metre()&gt;, mp_units::quantity&lt;mp_units::si::second()&gt;)':\nerror: could not convert 'mp_units::operator*&lt;si::metre(), double, si::second(), double&gt;(d, t)' from 'quantity&lt;mp_units::derived_unit&lt;mp_units::si::metre, mp_units::si::second&gt;(),[...]&gt;' to 'quantity&lt;mp_units::derived_unit&lt;mp_units::si::metre, mp_units::per&lt;mp_units::si::second&gt; &gt;(),[...]&gt;'\n 11 | return d * t;\n | ~~^~~\n | |\n | quantity&lt;mp_units::derived_unit&lt;mp_units::si::metre, mp_units::si::second&gt;(),[...]&gt;\n</code></pre>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#typed-quantities","title":"Typed quantities","text":"<p>Simple mode is all about and just about units. In case you care about a specific quantity type, typed quantities should be preferred. With this mode, for example, you can specify if you deal with <code>width</code>, <code>height</code>, or <code>radius</code> and ensure you will not assign one to another by accident.</p> <p>The previous example can be re-typed using typed quantities in the following way:</p> <pre><code>#include &lt;mp-units/ostream.h&gt;\n#include &lt;mp-units/systems/isq/space_and_time.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;iostream&gt;\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\nconstexpr quantity&lt;isq::speed[m / s]&gt; avg_speed(quantity&lt;isq::length[m]&gt; d,\nquantity&lt;isq::time[s]&gt; t)\n{\nreturn d / t;\n}\nint main()\n{\nconst quantity distance = isq::distance(110 * km);\nconst quantity duration = isq::time(2 * h);\nconst quantity speed = avg_speed(distance, duration);\nstd::cout &lt;&lt; \"A car driving \" &lt;&lt; distance &lt;&lt; \" in \" &lt;&lt; duration\n&lt;&lt; \" has an average speed of \" &lt;&lt; speed\n&lt;&lt; \" (\" &lt;&lt; speed.in(km / h) &lt;&lt; \")\\n\";\n}\n</code></pre> <pre><code>A car driving 110 km in 2 h has an average speed of 15.2778 m/s (55 km/h)\n</code></pre> <p>Try it on Compiler Explorer</p> <p>In case we will accidentally make the same calculation error as before, this time, we will get a bit longer error message also containing information about the quantity type:</p> <pre><code>In function 'constexpr mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::speed(), mp_units::derived_unit&lt;mp_units::si::metre, mp_units::per&lt;mp_units::si::second&gt; &gt;()&gt;()&gt; avg_speed(mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::length(), mp_units::si::metre()&gt;()&gt;, mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::time(), mp_units::si::second()&gt;()&gt;)':\nerror: could not convert 'mp_units::operator*&lt;reference&lt;isq::length(), si::metre()&gt;(), double, reference&lt;isq::time(), si::second()&gt;(), double&gt;(d, t)' from 'quantity&lt;mp_units::reference&lt;mp_units::derived_quantity_spec&lt;mp_units::isq::length, mp_units::isq::time&gt;(), mp_units::derived_unit&lt;mp_units::si::metre, mp_units::si::second&gt;()&gt;(),[...]&gt;' to 'quantity&lt;mp_units::reference&lt;mp_units::isq::speed(), mp_units::derived_unit&lt;mp_units::si::metre, mp_units::per&lt;mp_units::si::second&gt; &gt;()&gt;(),[...]&gt;'\n 12 | return d * t;\n | ~~^~~\n | |\n | quantity&lt;mp_units::reference&lt;mp_units::derived_quantity_spec&lt;mp_units::isq::length, mp_units::isq::time&gt;(), mp_units::derived_unit&lt;mp_units::si::metre, mp_units::si::second&gt;()&gt;(),[...]&gt;\n</code></pre> <p>As we can see above, the compilation error is longer but still relatively easy to understand.</p>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#additional-type-safety-with-typed-quantities","title":"Additional type safety with typed quantities","text":"<p>Based on the previous example, it might seem that typed quantities are not that useful, more to type and provide harder-to-understand error messages. It might be true in some cases, but there are cases where they provide an additional level of safety.</p> <p>Let's see another example:</p> SimpleTyped <pre><code>#include &lt;mp-units/math.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;numbers&gt;\nusing namespace mp_units;\nclass StorageTank {\nquantity&lt;square(si::metre)&gt; base_;\nquantity&lt;si::metre&gt; height_;\npublic:\nconstexpr StorageTank(const quantity&lt;square(si::metre)&gt;&amp; base,\nconst quantity&lt;si::metre&gt;&amp; height) :\nbase_(base), height_(height)\n{\n}\n// ...\n};\nclass CylindricalStorageTank : public StorageTank {\npublic:\nconstexpr CylindricalStorageTank(const quantity&lt;si::metre&gt;&amp; radius,\nconst quantity&lt;si::metre&gt;&amp; height) :\nStorageTank(std::numbers::pi * pow&lt;2&gt;(radius), height)\n{\n}\n};\nclass RectangularStorageTank : public StorageTank {\npublic:\nconstexpr RectangularStorageTank(const quantity&lt;si::metre&gt;&amp; length,\nconst quantity&lt;si::metre&gt;&amp; width,\nconst quantity&lt;si::metre&gt;&amp; height) :\nStorageTank(length * width, height)\n{\n}\n};\nint main()\n{\nusing namespace mp_units::si::unit_symbols;\nauto tank = RectangularStorageTank(1'000 * mm, 500 * mm, 200 * mm);\n// ...\n}\n</code></pre> <pre><code>#include &lt;mp-units/math.h&gt;\n#include &lt;mp-units/systems/isq/space_and_time.h&gt;\n#include &lt;mp-units/systems/si/si.h&gt;\n#include &lt;numbers&gt;\nusing namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\n// add a custom quantity type of kind isq::length\ninline constexpr struct horizontal_length\n: quantity_spec&lt;isq::length&gt; {} horizontal_length;\n// add a custom derived quantity type of kind isq::area\n// with a constrained quantity equation\ninline constexpr struct horizontal_area\n: quantity_spec&lt;isq::area, horizontal_length * isq::width&gt; {} horizontal_area;\nclass StorageTank {\nquantity&lt;horizontal_area[m2]&gt; base_;\nquantity&lt;isq::height[m]&gt; height_;\npublic:\nconstexpr StorageTank(const quantity&lt;horizontal_area[m2]&gt;&amp; base,\nconst quantity&lt;isq::height[m]&gt;&amp; height) :\nbase_(base), height_(height)\n{\n}\n// ...\n};\nclass CylindricalStorageTank : public StorageTank {\npublic:\nconstexpr CylindricalStorageTank(const quantity&lt;isq::radius[m]&gt;&amp; radius,\nconst quantity&lt;isq::height[m]&gt;&amp; height) :\nStorageTank(quantity_cast&lt;horizontal_area&gt;(std::numbers::pi * pow&lt;2&gt;(radius)),\nheight)\n{\n}\n};\nclass RectangularStorageTank : public StorageTank {\npublic:\nconstexpr RectangularStorageTank(const quantity&lt;horizontal_length[m]&gt;&amp; length,\nconst quantity&lt;isq::width[m]&gt;&amp; width,\nconst quantity&lt;isq::height[m]&gt;&amp; height) :\nStorageTank(length * width, height)\n{\n}\n};\nint main()\n{\nauto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\nisq::width(500 * mm),\nisq::height(200 * mm));\n// ...\n}\n</code></pre> <p>In the above example, the highlighted call doesn't look that safe anymore in the case of simple quantities, right? Suppose someone, either by mistake or due to some refactoring, will call the function with invalid order of arguments. In that case, the program will compile fine but not work as expected.</p> <p>Let's see what will happen if we reorder the arguments in the case of typed quantities:</p> <pre><code>auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),\nisq::height(200 * mm),\nisq::width(500 * mm));\n</code></pre> <p>This time a compiler provides the following compilation error:</p> <pre><code>In function 'int main()':\nerror: no matching function for call to 'RectangularStorageTank::RectangularStorageTank(mp_units::quantity&lt;mp_units::reference&lt;horizontal_length(), mp_units::si::milli_&lt;mp_units::si::metre()&gt;()&gt;(), int&gt;, mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::height(), mp_units::si::milli_&lt;mp_units::si::metre()&gt;()&gt;(), int&gt;, mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::width(), mp_units::si::milli_&lt;mp_units::si::metre()&gt;()&gt;(), int&gt;)'\n 47 | isq::width(500 * mm));\n | ^\nnote: candidate: 'constexpr RectangularStorageTank::RectangularStorageTank(const mp_units::quantity&lt;mp_units::reference&lt;horizontal_length(), mp_units::si::metre()&gt;()&gt;&amp;, const mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::width(), mp_units::si::metre()&gt;()&gt;&amp;, const mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::height(), mp_units::si::metre()&gt;()&gt;&amp;)'\n 35 | constexpr RectangularStorageTank(const quantity&lt;horizontal_length[m]&gt;&amp; length,\n | ^~~~~~~~~~~~~~~~~~~~~~\nnote: no known conversion for argument 2 from 'mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::height(), mp_units::si::milli_&lt;mp_units::si::metre()&gt;()&gt;(), int&gt;' to 'const mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::width(), mp_units::si::metre()&gt;()&gt;&amp;'\n 36 | const quantity&lt;isq::width[m]&gt;&amp; width,\n | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~\n</code></pre> <p>What about derived quantities? In the above example, you probably noticed that we also defined a custom <code>horizontal_area</code> quantity of kind <code>isq::area</code>. This quantity has the special property of being implicitly constructible only from the result of the multiplication of quantities of <code>horizontal_area</code> and <code>isq::width</code> or the ones that implicitly convert to them.</p> <p>Based on the above error message, we already know that a quantity of <code>isq::height</code> is not implicitly constructible to the quantity of <code>isq::width</code>. This property is transitively passed to derived quantities using them. If by accident, we will try to create a <code>StorageTank</code> base class in the following way:</p> <pre><code>class RectangularStorageTank : public StorageTank {\npublic:\nconstexpr RectangularStorageTank(const quantity&lt;horizontal_length[m]&gt;&amp; length,\nconst quantity&lt;isq::width[m]&gt;&amp; width,\nconst quantity&lt;isq::height[m]&gt;&amp; height) :\nStorageTank(length * height, height)\n{\n}\n};\n</code></pre> <p>we will again get a compilation error message like this one:</p> <pre><code>In constructor 'constexpr RectangularStorageTank::RectangularStorageTank(const mp_units::quantity&lt;mp_units::reference&lt;horizontal_length(), mp_units::si::metre()&gt;()&gt;&amp;, const mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::width(), mp_units::si::metre()&gt;()&gt;&amp;, const mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::height(), mp_units::si::metre()&gt;()&gt;&amp;)':\nerror: no matching function for call to 'StorageTank::StorageTank(mp_units::quantity&lt;mp_units::reference&lt;mp_units::derived_quantity_spec&lt;horizontal_length, mp_units::isq::height&gt;(), mp_units::derived_unit&lt;mp_units::power&lt;mp_units::si::metre, 2&gt; &gt;()&gt;(), double&gt;, const mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::height(), mp_units::si::metre()&gt;()&gt;&amp;)'\n 39 | StorageTank(length * height, height)\n | ^\nnote: candidate: 'constexpr StorageTank::StorageTank(const mp_units::quantity&lt;mp_units::reference&lt;horizontal_area(), mp_units::derived_unit&lt;mp_units::power&lt;mp_units::si::metre, 2&gt; &gt;()&gt;()&gt;&amp;, const mp_units::quantity&lt;mp_units::reference&lt;mp_units::isq::height(), mp_units::si::metre()&gt;()&gt;&amp;)'\n 16 | constexpr StorageTank(const quantity&lt;horizontal_area[m2]&gt;&amp; base,\n | ^~~~~~~~~~~\n&lt;source&gt;:16:62: note: no known conversion for argument 1 from 'mp_units::quantity&lt;mp_units::reference&lt;mp_units::derived_quantity_spec&lt;horizontal_length, mp_units::isq::height&gt;(), mp_units::derived_unit&lt;mp_units::power&lt;mp_units::si::metre, 2&gt; &gt;()&gt;(), double&gt;' to 'const mp_units::quantity&lt;mp_units::reference&lt;horizontal_area(), mp_units::derived_unit&lt;mp_units::power&lt;mp_units::si::metre, 2&gt; &gt;()&gt;()&gt;&amp;'\n 16 | constexpr StorageTank(const quantity&lt;horizontal_area[m2]&gt;&amp; base,\n | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~\n</code></pre> <p>Tip</p> <p>If you need to use various quantities of the same kind, consider using typed quantities to bring an additional level of safety to your project.</p>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#quantity_cast-to-force-unsafe-conversions","title":"<code>quantity_cast()</code> to force unsafe conversions","text":"<p>Did you notice the <code>quantity_cast()</code> usage in the other child class?</p> <pre><code>class CylindricalStorageTank : public StorageTank {\npublic:\nconstexpr CylindricalStorageTank(const quantity&lt;isq::radius[m]&gt;&amp; radius,\nconst quantity&lt;isq::height[m]&gt;&amp; height) :\nStorageTank(quantity_cast&lt;horizontal_area&gt;(std::numbers::pi * pow&lt;2&gt;(radius)),\nheight)\n{\n}\n};\n</code></pre> <p>As <code>isq::radius</code> is not convertible to either a <code>horizontal_length</code> or <code>isq::width</code>, the derived quantity of <code>pow&lt;2&gt;(radius)</code> can't be converted to <code>horizontal_area</code> as well. It would be unsafe to allow such a conversion as not all of the circles lie flat on the ground, right?</p> <p>In such a case, the user has to explicitly force such an unsafe conversion with the help of a <code>quantity_cast()</code>. This function name is easy to spot in code reviews or while searching the project for problems if something goes sideways. In case of unexpected issues related to quantities, this should be the first function to look for.</p> <p>Tip</p> <p>Do not overuse <code>quantity_cast()</code>. Use it only when necessary and ensure that the requested conversion is exactly what you need in this case.</p>"},{"location":"users_guide/framework_basics/simple_and_typed_quantities/#which-mode-to-use-in-my-project","title":"Which mode to use in my project?","text":"<p>In case you wonder which mode you should choose for your project, we have good news for you. Simple and typed quantity modes can be freely mixed with each other. When you use different quantities of the same kind (i.e. radius, wavelength, altitude, ...), you should probably reach for typed quantities to bring additional safety for those cases. Otherwise, just use simple mode for the remaining quantities. The mp-units library will do its best to protect your project based on the information provided.</p> <p>Tip</p> <p>You can easily mix simple and typed quantities in your project.</p>"},{"location":"users_guide/framework_basics/systems_of_quantities/","title":"Systems of Quantities","text":"<p>The physical units libraries on the market typically only scope on modeling one or more systems of units. However, this is not the only system kind to model. Another, and maybe even more important, system kind is a system of quantities.</p> <p>Info</p> <p>Please note that the mp-units is probably the first library on the Open Source market (in any programming language) that models the ISQ with all its definitions provided in ISO 80000. Please provide feedback if something looks odd or could be improved.</p>"},{"location":"users_guide/framework_basics/systems_of_quantities/#dimension-is-not-enough-to-describe-a-quantity","title":"Dimension is not enough to describe a quantity","text":"<p>Most of the products on the market are aware of physical dimensions. However, a dimension is not enough to describe a quantity. For example, let's see the following implementation:</p> <pre><code>class Box {\narea base_;\nlength height_;\npublic:\nBox(length l, length w, length h) : base_(l * w), height_(h) {}\n// ...\n};\nBox my_box(2 * m, 3 * m, 1 * m);\n</code></pre> <p>How do you like such an interface? It turns out that in most existing strongly-typed libraries this is often the best we can do </p> <p>Another typical question many users ask is how to deal with energy and moment of force. Both of those have the same dimension but are different quantities.</p> <p>Another question is what should be the result of:</p> <pre><code>auto res = 1 * Hz + 1 * Bq + 1 * Bd;\n</code></pre> <p>where:</p> <ul> <li><code>Hz</code> (hertz) - unit of frequency</li> <li><code>Bq</code> (becquerel) - unit of activity</li> <li><code>Bd</code> (baud) - unit of modulation rate</li> </ul> <p>All of those quantities have the same dimension, namely \\(\\mathsf{T}^{-1}\\), but probably it is not wise to allow adding, subtracting, or comparing them, as they describe vastly different physical properties.</p> <p>Important</p> <p>More than one quantity may be defined for the same dimension:</p> <ul> <li>quantities of different kinds (i.e. frequency, modulation rate, activity, ...)</li> <li>quantities of the same kind (i.e. length, width, altitude, distance, radius, wavelength, position vector, ...)</li> </ul> <p>It turns out that the above issues can't be solved correctly without proper modeling of a system of quantities.</p>"},{"location":"users_guide/framework_basics/systems_of_quantities/#quantities-of-the-same-kind","title":"Quantities of the same kind","text":"<p>ISO 80000-1</p> <ul> <li>Quantities may be grouped together into categories of quantities that are mutually comparable</li> <li>Mutually comparable quantities are called quantities of the same kind</li> <li>Two or more quantities cannot be added or subtracted unless they belong to the same category of mutually comparable quantities</li> <li>Quantities of the same kind within a given system of quantities have the same quantity dimension</li> <li>Quantities of the same dimension are not necessarily of the same kind</li> </ul> <p>The above quotes from ISO 80000 answer to all the issues above. Two quantities can't be added, subtracted, or compared unless they belong to the same kind. As frequency, activity, and modulation rate are different kinds, the expression provided above should not compile.</p>"},{"location":"users_guide/framework_basics/systems_of_quantities/#system-of-quantities-is-not-only-about-kinds","title":"System of quantities is not only about kinds","text":"<p>ISO 80000 specify hundreds of different quantities. There are plenty of different kinds provided and often each kind contains more than one quantity. In fact, it turns out that such quantities form a hierarchy of quantities of the same kind.</p> <p>For example, here are all quantities of the kind length provided in the ISO 80000:</p> <pre><code>flowchart TD\n length --- width[width, breadth]\n length --- height[height, depth, altitude]\n width --- thickness\n width --- diameter\n width --- radius\n length --- path_length\n path_length --- distance\n distance --- radial_distance\n length --- wavelength\n length --- position_vector[\"position_vector\\n{vector}\"]\n length --- displacement[\"displacement\\n{vector}\"]\n radius --- radius_of_curvature</code></pre> <p>Each of the above quantities expresses some kind of length, and each can be measured with <code>si::metre</code>. However, each of them has different properties, usage, and sometimes even a different representation type (notice that <code>position_vector</code> and <code>displacement</code> are vector quantities).</p> <p>Analyzing such a hierarchy can help us in defining arithmetics and conversion rules.</p>"},{"location":"users_guide/framework_basics/systems_of_quantities/#defining-quantities","title":"Defining quantities","text":"<p>In the mp-units library all the information about the quantity is provided with the <code>quantity_spec</code> class template. In order to define a specific quantity a user should inherit a strong type from such an instantiation.</p> <p>Tip</p> <p>Quantity specification definitions benefit from an explicit object parameter added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code. However, as C++23 is far from being mainstream today, a portability macro <code>QUANTITY_SPEC()</code> is provided and used consistently through the library to allow the code to compile with C++20 compilers, thanks to the CRTP usage under the hood.</p> <p>For example, here is how the above quantity kind tree can be modeled in the library:</p> C++23C++20Portable <pre><code>inline constexpr struct length : quantity_spec&lt;dim_length&gt; {} length;\ninline constexpr struct width : quantity_spec&lt;length&gt; {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height : quantity_spec&lt;length&gt; {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness : quantity_spec&lt;width&gt; {} thickness;\ninline constexpr struct diameter : quantity_spec&lt;width&gt; {} diameter;\ninline constexpr struct radius : quantity_spec&lt;width&gt; {} radius;\ninline constexpr struct radius_of_curvature : quantity_spec&lt;radius&gt; {} radius_of_curvature;\ninline constexpr struct path_length : quantity_spec&lt;length&gt; {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance : quantity_spec&lt;path_length&gt; {} distance;\ninline constexpr struct radial_distance : quantity_spec&lt;distance&gt; {} radial_distance;\ninline constexpr struct wavelength : quantity_spec&lt;length&gt; {} wavelength;\ninline constexpr struct position_vector : quantity_spec&lt;length, quantity_character::vector&gt; {} position_vector;\ninline constexpr struct displacement : quantity_spec&lt;length, quantity_character::vector&gt; {} displacement;\n</code></pre> <pre><code>inline constexpr struct length : quantity_spec&lt;length, dim_length&gt; {} length;\ninline constexpr struct width : quantity_spec&lt;width, length&gt; {} width;\ninline constexpr auto breadth = width;\ninline constexpr struct height : quantity_spec&lt;height, length&gt; {} height;\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\ninline constexpr struct thickness : quantity_spec&lt;thickness, width&gt; {} thickness;\ninline constexpr struct diameter : quantity_spec&lt;diameter, width&gt; {} diameter;\ninline constexpr struct radius : quantity_spec&lt;radius, width&gt; {} radius;\ninline constexpr struct radius_of_curvature : quantity_spec&lt;radius_of_curvature, radius&gt; {} radius_of_curvature;\ninline constexpr struct path_length : quantity_spec&lt;path_length, length&gt; {} path_length;\ninline constexpr auto arc_length = path_length;\ninline constexpr struct distance : quantity_spec&lt;distance, path_length&gt; {} distance;\ninline constexpr struct radial_distance : quantity_spec&lt;radial_distance, distance&gt; {} radial_distance;\ninline constexpr struct wavelength : quantity_spec&lt;wavelength, length&gt; {} wavelength;\ninline constexpr struct position_vector : quantity_spec&lt;position_vector, length, quantity_character::vector&gt; {} position_vector;\ninline constexpr struct displacement : quantity_spec&lt;displacement, length, quantity_character::vector&gt; {} displacement;\n</code></pre> <pre><code>QUANTITY_SPEC(length, dim_length);\nQUANTITY_SPEC(width, length);\ninline constexpr auto breadth = width;\nQUANTITY_SPEC(height, length);\ninline constexpr auto depth = height;\ninline constexpr auto altitude = height;\nQUANTITY_SPEC(thickness, width);\nQUANTITY_SPEC(diameter, width);\nQUANTITY_SPEC(radius, width);\nQUANTITY_SPEC(radius_of_curvature, radius);\nQUANTITY_SPEC(path_length, length);\ninline constexpr auto arc_length = path_length;\nQUANTITY_SPEC(distance, path_length);\nQUANTITY_SPEC(radial_distance, distance);\nQUANTITY_SPEC(wavelength, length);\nQUANTITY_SPEC(position_vector, length, quantity_character::vector);\nQUANTITY_SPEC(displacement, length, quantity_character::vector);\n</code></pre> <p>Note</p> <p>More information on how to define a system of quantities can be found in the \"International System of Quantities (ISQ)\" chapter.</p>"},{"location":"users_guide/framework_basics/systems_of_quantities/#comparing-adding-and-subtracting-quantities","title":"Comparing, adding, and subtracting quantities","text":"<p>ISO 80000 explicitly states that <code>width</code> and <code>height</code> are quantities of the same kind, and as such they:</p> <ul> <li>are mutually comparable</li> <li>can be added and subtracted</li> </ul> <p>If we take the above for granted, the only reasonable result of <code>1 * width + 1 * height</code> is <code>2 * length</code>, where the result of <code>length</code> is known as a common quantity type. A result of such an equation is always the first common branch in a hierarchy tree of the same kind. For example:</p> <pre><code>static_assert(common_quantity_spec(isq::width, isq::height) == isq::length);\nstatic_assert(common_quantity_spec(isq::thickness, isq::radius) == isq::width);\nstatic_assert(common_quantity_spec(isq::distance, isq::path_length) == isq::path_length);\n</code></pre>"},{"location":"users_guide/framework_basics/systems_of_quantities/#converting-between-quantities","title":"Converting between quantities","text":"<p>Based on the same hierarchy of quantities of kind length, we can define quantity conversion rules.</p> <ol> <li> <p>Implicit conversions</p> <ul> <li>every <code>width</code> is a <code>length</code></li> <li>every <code>radius</code> is a <code>width</code></li> </ul> <pre><code>static_assert(implicitly_convertible(isq::width, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::length));\nstatic_assert(implicitly_convertible(isq::radius, isq::width));\n</code></pre> </li> <li> <p>Explicit conversions</p> <ul> <li>not every <code>length</code> is a <code>width</code></li> <li>not every <code>width</code> is a <code>radius</code></li> </ul> <pre><code>static_assert(!implicitly_convertible(isq::length, isq::width));\nstatic_assert(!implicitly_convertible(isq::length, isq::radius));\nstatic_assert(!implicitly_convertible(isq::width, isq::radius));\nstatic_assert(explicitly_convertible(isq::length, isq::width));\nstatic_assert(explicitly_convertible(isq::length, isq::radius));\nstatic_assert(explicitly_convertible(isq::width, isq::radius));\n</code></pre> </li> <li> <p>Explicit casts</p> <ul> <li><code>height</code> is not a <code>width</code></li> <li>both <code>height</code> and <code>width</code> are quantities of kind <code>length</code></li> </ul> <pre><code>static_assert(!implicitly_convertible(isq::height, isq::width));\nstatic_assert(!explicitly_convertible(isq::height, isq::width));\nstatic_assert(castable(isq::height, isq::width));\n</code></pre> </li> <li> <p>No conversion</p> <ul> <li><code>time</code> has nothing in common with <code>length</code></li> </ul> <pre><code>static_assert(!implicitly_convertible(isq::time, isq::length));\nstatic_assert(!explicitly_convertible(isq::time, isq::length));\nstatic_assert(!castable(isq::time, isq::length));\n</code></pre> </li> </ol>"},{"location":"users_guide/framework_basics/systems_of_quantities/#hierarchies-of-derived-quantities","title":"Hierarchies of derived quantities","text":"<p>Derived quantity equations often do not automatically form a hierarchy tree. This is why it is sometimes not obvious what such a tree should look like. Also, ISO explicitly states:</p> <p>ISO/IEC Guide 99</p> <p>The division of \u2018quantity\u2019 according to \u2018kind of quantity\u2019 is, to some extent, arbitrary.</p> <p>The below presents some arbitrary hierarchy of derived quantities of kind energy:</p> <pre><code>flowchart TD\n energy[\"energy\\n(mass * length^2 / time^2)\"]\n energy --- mechanical_energy\n mechanical_energy --- potential_energy\n potential_energy --- gravitational_potential_energy[\"gravitational_potential_energy\\n(mass / acceleration_of_free_fall / height)\"]\n potential_energy --- elastic_potential_energy[\"elastic_potential_energy\\n(spring_constant * amount_of_compression^2)\"]\n mechanical_energy --- kinetic_energy[\"kinetic_energy\\n(mass * speed^2)\"]\n energy --- enthalpy\n enthalpy --- internal_energy[internal_energy, thermodynamic_energy]\n internal_energy --- Helmholtz_energy[Helmholtz_energy, Helmholtz_function]\n enthalpy --- Gibbs_energy[Gibbs_energy, Gibbs_function]\n energy --- active_energy</code></pre> <p>Notice, that even though all of those quantities have the same dimension and can be expressed in the same units, they have different quantity equations used to create them implicitly:</p> <ul> <li> <p><code>energy</code> is the most generic one and thus can be created from base quantities of <code>mass</code>, <code>length</code>, and <code>time</code>. As those are also the roots of quantities of their kinds and all other quantities are implicitly convertible to them (we agreed on that \"every <code>width</code> is a <code>length</code>\" already), it means that an <code>energy</code> can be implicitly constructed from any quantity of mass, length, and time.</p> <pre><code>static_assert(implicitly_convertible(isq::mass * pow&lt;2&gt;(isq::length) / pow&lt;2&gt;(isq::time), isq::energy));\nstatic_assert(implicitly_convertible(isq::mass * pow&lt;2&gt;(isq::height) / pow&lt;2&gt;(isq::time), isq::energy));\n</code></pre> </li> <li> <p><code>mechanical_energy</code> is a more \"specialized\" quantity than <code>energy</code> (not every <code>energy</code> is a <code>mechanical_energy</code>). It is why an explicit cast is needed to convert from either <code>energy</code> or the results of its quantity equation.</p> <pre><code>static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::energy, isq::mechanical_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow&lt;2&gt;(isq::length) / pow&lt;2&gt;(isq::time),\nisq::mechanical_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow&lt;2&gt;(isq::length) / pow&lt;2&gt;(isq::time),\nisq::mechanical_energy));\n</code></pre> </li> <li> <p><code>gravitational_potential_energy</code> is not only even more specialized one but additionally, it is special in a way that it provides its own \"constrained\" quantity equation. Maybe not every <code>mass * pow&lt;2&gt;(length) / pow&lt;2&gt;(time)</code> is a <code>gravitational_potential_energy</code>, but every <code>mass * acceleration_of_free_fall * height</code> is.</p> <pre><code>static_assert(!implicitly_convertible(isq::energy, gravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::energy, gravitational_potential_energy));\nstatic_assert(!implicitly_convertible(isq::mass * pow&lt;2&gt;(isq::length) / pow&lt;2&gt;(isq::time),\ngravitational_potential_energy));\nstatic_assert(explicitly_convertible(isq::mass * pow&lt;2&gt;(isq::length) / pow&lt;2&gt;(isq::time),\ngravitational_potential_energy));\nstatic_assert(implicitly_convertible(isq::mass * isq::acceleration_of_free_fall * isq::height,\ngravitational_potential_energy));\n</code></pre> </li> </ul>"},{"location":"users_guide/framework_basics/systems_of_quantities/#modeling-a-quantity-kind","title":"Modeling a quantity kind","text":"<p>In the physical units library, we also need an abstraction describing an entire family of quantities of the same kind. Such quantities have not only the same dimension but also can be expressed in the same units.</p> <p>To annotate a quantity to represent its kind (and not just a hierarchy tree's root quantity) we introduced a <code>kind_of&lt;&gt;</code> specifier. For example, to express any quantity of length, we need to type <code>kind_of&lt;isq::length&gt;</code>.</p> <p>Important</p> <p><code>isq::length</code> and <code>kind_of&lt;isq::length&gt;</code> are two different things.</p> <p>Such an entity behaves as any quantity of its kind. This means that it is implicitly convertible to any quantity in a tree.</p> <pre><code>static_assert(!implicitly_convertible(isq::length, isq::height));\nstatic_assert(implicitly_convertible(kind_of&lt;isq::length&gt;, isq::height));\n</code></pre> <p>Additionally, the result of operations on quantity kinds is also a quantity kind:</p> <pre><code>static_assert(same_type&lt;kind_of&lt;isq::length&gt; / kind_of&lt;isq::time&gt;, kind_of&lt;isq::length / isq::time&gt;&gt;);\n</code></pre> <p>However, if at least one equation's operand is not a kind, the result becomes a \"strong\" quantity where all the kinds are converted to the hierarchy tree's root quantities:</p> <pre><code>static_assert(!same_type&lt;kind_of&lt;isq::length&gt; / isq::time, kind_of&lt;isq::length / isq::time&gt;&gt;);\nstatic_assert(same_type&lt;kind_of&lt;isq::length&gt; / isq::time, isq::length / isq::time&gt;);\n</code></pre> <p>Info</p> <p>Only a root quantity from the hierarchy tree or the one marked with <code>is_kind</code> specifier in the <code>quantity_spec</code> definition can be put as a template parameter to the <code>kind_of</code> specifier. For example, <code>kind_of&lt;isq::width&gt;</code> will fail to compile.</p>"},{"location":"users_guide/framework_basics/systems_of_units/","title":"Systems of Units","text":"<p>Modeling a system of units is probably the most important feature and a selling point of every physical units library. Thanks to that, the library can protect users from performing invalid operations on quantities and provide automated conversion factors between various compatible units.</p> <p>Probably all the libraries in the wild model the SI and many of them provide support for additional units belonging to various other systems (i.e. imperial).</p>"},{"location":"users_guide/framework_basics/systems_of_units/#systems-of-units-are-based-on-systems-of-quantities","title":"Systems of Units are based on Systems of Quantities","text":"<p>Systems of quantities specify a set of quantities and equations relating to those quantities. Those equations do not take any unit or a numerical representation into account at all. In order to create a quantity, we need to add those missing pieces of information. This is where a system of units kicks in.</p> <p>The SI is explicitly stated to be based on the ISQ. Among others, it defines <code>7</code> base units, one for each base quantity. In the mp-units this is expressed by associating a quantity kind (that we discussed in detail in the previous chapter) with a unit that is used to express it:</p> <pre><code>inline constexpr struct metre : named_unit&lt;\"m\", kind_of&lt;isq::length&gt;&gt; {} metre;\n</code></pre> <p>Important</p> <p>The <code>kind_of&lt;isq::length&gt;</code> above states explicitly that this unit has an associated quantity kind. In other words, <code>si::metre</code> (and scaled units based on it) can be used to express the amount of any quantity of kind length.</p>"},{"location":"users_guide/framework_basics/systems_of_units/#units-compose","title":"Units compose","text":"<p>One of the strongest points of the SI system is that its units compose. This allows providing thousands of different units for hundreds of various quantities with a really small set of predefined units and prefixes.</p> <p>The same is modeled in the mp-units library, which also allows composing predefined units to create a nearly infinite number of different derived units. For example, one can write:</p> <pre><code>quantity&lt;si::metre / si::second&gt; q;\n</code></pre> <p>to express a quantity of speed. The resulting quantity type is implicitly inferred from the unit equation by repeating exactly the same operations on the associated quantity kinds.</p>"},{"location":"users_guide/framework_basics/systems_of_units/#many-shades-of-the-same-unit","title":"Many shades of the same unit","text":"<p>The SI provides the names for 22 common coherent units of 22 derived quantities.</p> <p>Each such named derived unit is a result of a specific predefined unit equation. For example, a unit of power quantity is defined in the library as:</p> <pre><code>inline constexpr struct watt : named_unit&lt;\"W\", joule / second&gt; {} watt;\n</code></pre> <p>However, a power quantity can be expressed in other units as well. For example, the following:</p> <pre><code>auto q1 = 42 * W;\nstd::cout &lt;&lt; q1 &lt;&lt; \"\\n\";\nstd::cout &lt;&lt; q1.in(J / s) &lt;&lt; \"\\n\";\nstd::cout &lt;&lt; q1.in(N * m / s) &lt;&lt; \"\\n\";\nstd::cout &lt;&lt; q1.in(kg * m2 / s3) &lt;&lt; \"\\n\";\n</code></pre> <p>prints:</p> <pre><code>42 W\n42 J/s\n42 N m/s\n42 kg m\u00b2/s\u00b3\n</code></pre> <p>All of the above quantities are equivalent and mean exactly the same.</p>"},{"location":"users_guide/framework_basics/systems_of_units/#constraining-a-derived-unit-to-work-only-with-a-specific-derived-quantity","title":"Constraining a derived unit to work only with a specific derived quantity","text":"<p>Some derived units are valid only for specific derived quantities. For example, SI specifies both <code>hertz</code> and <code>becquerel</code> derived units with the same unit equation <code>1 / s</code>. However, it also explicitly states:</p> <p>SI Brochure</p> <p>The hertz shall only be used for periodic phenomena and the becquerel shall only be used for stochastic processes in activity referred to a radionuclide.</p> <p>The library allows constraining such units in the following way:</p> <pre><code>inline constexpr struct hertz : named_unit&lt;\"Hz\", 1 / second, kind_of&lt;isq::frequency&gt;&gt; {} hertz;\ninline constexpr struct becquerel : named_unit&lt;\"Bq\", 1 / second, kind_of&lt;isq::activity&gt;&gt; {} becquerel;\n</code></pre> <p>With the above, <code>hertz</code> can only be used for frequencies while becquerel should only be used for quantities of activity. This means that the following equation will not compile:</p> <pre><code>auto q = 1 * Hz + 1 * Bq; // Fails to compile\n</code></pre> <p>This is exactly what we wanted to achieve to improve the type-safety of the library.</p>"},{"location":"users_guide/framework_basics/systems_of_units/#prefixed-units","title":"Prefixed units","text":"<p>Besides named units, the SI specifies also 24 prefixes (all being a power of <code>10</code>) that can be prepended to all named units to obtain various scaled versions of them.</p> <p>Implementation of <code>std::ratio</code> provided by all major compilers is able to express only 16 of them. This is why, in the mp-units, we had to find an alternative way to represent unit magnitude in a more flexible way.</p> <p>Each prefix is implemented as:</p> <pre><code>template&lt;PrefixableUnit auto U&gt; struct quecto_ : prefixed_unit&lt;\"q\", mag_power&lt;10, -30&gt;, U&gt; {};\ntemplate&lt;PrefixableUnit auto U&gt; inline constexpr quecto_&lt;U&gt; quecto;\n</code></pre> <p>and then a PrefixableUnit can be prefixed in the following way:</p> <pre><code>inline constexpr auto qm = quecto&lt;metre&gt;;\n</code></pre> <p>The usage of <code>mag_power</code> not only enables providing support for SI prefixes but it can also efficiently represent any rational magnitude. For example, IEC 80000 prefixes used in the IT industry can be implemented as:</p> <pre><code>template&lt;PrefixableUnit auto U&gt; struct yobi_ : prefixed_unit&lt;\"Yi\", mag_power&lt;2, 80&gt;, U&gt; {};\ntemplate&lt;PrefixableUnit auto U&gt; inline constexpr yobi_&lt;U&gt; yobi;\n</code></pre>"},{"location":"users_guide/framework_basics/systems_of_units/#scaled-units","title":"Scaled units","text":"<p>In the SI, all units are either base or derived units or prefixed versions of those. However, those are not the only options possible.</p> <p>For example, there is a list of off-system units accepted for use with SI. All of those are scaled versions of the SI units with ratios that can't be explicitly expressed with predefined SI prefixes. Those include units like minute, hour, or electronvolt:</p> <pre><code>inline constexpr struct minute : named_unit&lt;\"min\", mag&lt;60&gt; * si::second&gt; {} minute;\ninline constexpr struct hour : named_unit&lt;\"h\", mag&lt;60&gt; * minute&gt; {} hour;\ninline constexpr struct electronvolt : named_unit&lt;\"eV\", mag&lt;ratio{1'602'176'634, 1'000'000'000}&gt; * mag_power&lt;10, -19&gt; * si::joule&gt; {} electronvolt;\n</code></pre> <p>Also, units of other systems of units are often defined in terms of scaled versions of the SI units. For example, the international yard is defined as:</p> <pre><code>inline constexpr struct yard : named_unit&lt;\"yd\", mag&lt;ratio{9'144, 10'000}&gt; * si::metre&gt; {} yard;\n</code></pre> <p>For some units, a magnitude might also be irrational. The best example here is a <code>degree</code> which is defined using a floating-point magnitude having a factor of the number \u03c0 (Pi):</p> <pre><code>inline constexpr struct mag_pi : magnitude&lt;std::numbers::pi_v&lt;long double&gt;&gt; {} mag_pi;\n</code></pre> <pre><code>inline constexpr struct degree : named_unit&lt;basic_symbol_text{\"\u00b0\", \"deg\"}, mag_pi / mag&lt;180&gt; * si::radian&gt; {} degree;\n</code></pre>"},{"location":"users_guide/framework_basics/text_output/","title":"Text Output","text":"<p>Besides providing dimensional analysis and units conversions, the library also tries hard to print any quantity in the most user-friendly way.</p> <p>Note</p> <p>The library does not provide a text output for quantity points, as printing just a number and a unit is not enough to adequately describe a quantity point. Often an additional postfix is required.</p> <p>For example, the text output of <code>42 m</code> may mean many things and can also be confused with an output of a regular quantity. On the other hand, printing <code>42 m AMSL</code> for altitudes above mean sea level is a much better solution, but the library does not have enough information to print it that way by itself.</p>"},{"location":"users_guide/framework_basics/text_output/#customization-point","title":"Customization point","text":"<p>The SI Brochure says:</p> <p>SI Brochure</p> <p>The numerical value always precedes the unit and a space is always used to separate the unit from the number. ... The only exceptions to this rule are for the unit symbols for degree, minute and second for plane angle, <code>\u00b0</code>, <code>\u2032</code> and <code>\u2033</code>, respectively, for which no space is left between the numerical value and the unit symbol.</p> <p>To support the above, the library exposes <code>space_before_unit_symbol</code> customization point. By default, its value is <code>true</code> for all the units, so the space between a number and a unit will be present in the output text. To change this behavior, we have to provide a partial specialization for a specific unit:</p> <pre><code>template&lt;&gt;\ninline constexpr bool space_before_unit_symbol&lt;non_si::degree&gt; = false;\n</code></pre> <p>Note</p> <p>The above works only for the default formatting. In case we provide our own format specification (i.e. <code>std::format(\"{:%Q %q}\", q)</code>), the library will always obey this specification for all the units (no matter of what is the actual value of the <code>space_before_unit_symbol</code> customization point) and the separating space will always be present in this case.</p>"},{"location":"users_guide/framework_basics/text_output/#output-streams","title":"Output streams","text":"<p>Tip</p> <p>The output streaming support is opt-in and can be enabled by including the <code>&lt;mp-units/ostream.h&gt;</code> header file.</p> <p>The easiest way to print a quantity is to provide its object to the output stream:</p> <pre><code>using namespace mp_units;\nusing namespace mp_units::si::unit_symbols;\nusing namespace mp_units::international::unit_symbols;\nconst QuantityOf&lt;isq::speed&gt; auto v1 = avg_speed(220. * km, 2 * h);\nconst QuantityOf&lt;isq::speed&gt; auto v2 = avg_speed(140. * mi, 2 * h);\nstd::cout &lt;&lt; v1 &lt;&lt; '\\n'; // 110 km/h\nstd::cout &lt;&lt; v2 &lt;&lt; '\\n'; // 70 mi/h\n</code></pre> <p>The text output will always print the value of a quantity typically followed by a space and then the symbol of a unit associated with this quantity.</p> <p>Important</p> <p>Remember that when we deal with a quantity of an \"unknown\" <code>auto</code> type, it is a good practice to always convert the unit to the expected one before passing it to the text output:</p> <pre><code>std::cout &lt;&lt; v1.in(km / h) &lt;&lt; '\\n'; // 110 km/h\nstd::cout &lt;&lt; v1.force_in(m / s) &lt;&lt; '\\n'; // 30.5556 m/s\n</code></pre>"},{"location":"users_guide/framework_basics/text_output/#output-stream-formatting","title":"Output stream formatting","text":"<p>Only basic formatting can be applied for output streams. It includes control over width, fill, and alignment:</p> <pre><code>std::cout &lt;&lt; \"|\" &lt;&lt; std::setw(10) &lt;&lt; 123 * m &lt;&lt; \"|\\n\"; // | 123 m|\nstd::cout &lt;&lt; \"|\" &lt;&lt; std::setw(10) &lt;&lt; std::left &lt;&lt; 123 * m &lt;&lt; \"|\\n\"; // |123 m |\nstd::cout &lt;&lt; \"|\" &lt;&lt; std::setw(10) &lt;&lt; std::setfill('*') &lt;&lt; 123 * m &lt;&lt; \"|\\n\"; // |123 m*****|\n</code></pre>"},{"location":"users_guide/framework_basics/text_output/#stdformat","title":"<code>std::format</code>","text":"<p>Tip</p> <p>The text formatting facility support is opt-in and can be enabled by including the <code>&lt;mp-units/format.h&gt;</code> header file.</p> <p>The mp-units library provides custom formatters for <code>std::format</code> facility which allows fine-grained control over what and how it is being printed in the text output.</p>"},{"location":"users_guide/framework_basics/text_output/#grammar","title":"Grammar","text":"<pre><code>units-format-spec ::= [fill-and-align] [width] [units-specs]\nunits-specs ::= conversion-spec\n units-specs conversion-spec\n units-specs literal-char\nliteral-char ::= any character other than '{' or '}'\nconversion-spec ::= '%' units-type\nunits-type ::= [units-rep-modifier] 'Q'\n [units-unit-modifier] 'q'\nunits-rep-modifier ::= [sign] [#] [precision] [L] [units-rep-type]\nunits-rep-type ::= one of \"aAbBdeEfFgGoxX\"\nunits-unit-modifier ::= [units-text-encoding, units-unit-symbol-denominator, units-unit-symbol-separator]\nunits-text-encoding ::= one of \"UA\"\nunits-unit-symbol-solidus ::= one of \"oan\"\nunits-unit-symbol-separator ::= one of \"sd\"\n</code></pre> <p>In the above grammar:</p> <ul> <li><code>fill-and-align</code>, <code>width</code>, <code>sign</code>, <code>#</code>, <code>precision</code>, and <code>L</code> tokens, as well as the individual tokens of <code>units-rep-type</code> are defined in the format.string.std chapter of the C++ standard specification,</li> <li>tokens <code>Q</code> and <code>q</code> of <code>units-type</code> are described in the time.format chapter of the C++ standard specification,</li> <li><code>units-text-encoding</code> tokens specify the unit text encoding:<ul> <li><code>U</code> (default) uses the Unicode symbols defined by the SI specification (i.e. <code>m\u00b3</code>, <code>\u00b5s</code>)</li> <li><code>A</code> token forces non-standard ASCII-only output (i.e. <code>m^3</code>, <code>us</code>)</li> </ul> </li> <li><code>units-unit-symbol-solidus</code> tokens specify how the division of units should look like:<ul> <li><code>o</code> (default) outputs <code>/</code> only when there is only one unit in the denominator, otherwise negative exponents are printed (i.e. <code>m/s</code>, <code>kg m\u207b\u00b9 s\u207b\u00b9</code>)</li> <li><code>a</code> always uses solidus (i.e. <code>m/s</code>, <code>kg/(m s)</code>)</li> <li><code>n</code> never prints solidus, which means that negative exponents are always used (i.e. <code>m s\u207b\u00b9</code>, <code>kg m\u207b\u00b9 s\u207b\u00b9</code>)</li> </ul> </li> <li><code>units-unit-symbol-separator</code> tokens specify how multiplied unit symbols should be separated:<ul> <li><code>s</code> (default) uses space as a separator (i.e. <code>kg m\u00b2/s\u00b2</code>)</li> <li><code>d</code> uses half-high dot (<code>\u22c5</code>) as a separator (i.e. <code>kg\u22c5m\u00b2/s\u00b2</code>)</li> </ul> </li> </ul>"},{"location":"users_guide/framework_basics/text_output/#default-formatting","title":"Default formatting","text":"<p>To format <code>quantity</code> values the formatting facility uses <code>units-format-spec</code>. If it is left empty, the default formatting of <code>{:%Q %q}</code> is applied. The same default formatting is also applied to the output streams. This is why the following code lines produce the same output:</p> <pre><code>std::cout &lt;&lt; \"Distance: \" &lt;&lt; 123 * km &lt;&lt; \"\\n\";\nstd::cout &lt;&lt; std::format(\"Distance: {}\\n\", 123 * km);\nstd::cout &lt;&lt; std::format(\"Distance: {:%Q %q}\\n\", 123 * km);\n</code></pre>"},{"location":"users_guide/framework_basics/text_output/#controlling-width-fill-and-alignment","title":"Controlling width, fill, and alignment","text":"<p>To control width, fill, and alignment, the C++ standard grammar tokens <code>fill-and-align</code> and <code>width</code> are being used, and they treat a quantity value and symbol as a contiguous text:</p> <pre><code>std::println(\"|{:0}|\", 123 * m); // |123 m|\nstd::println(\"|{:10}|\", 123 * m); // | 123 m|\nstd::println(\"|{:&lt;10}|\", 123 * m); // |123 m |\nstd::println(\"|{:&gt;10}|\", 123 * m); // | 123 m|\nstd::println(\"|{:^10}|\", 123 * m); // | 123 m |\nstd::println(\"|{:*&lt;10}|\", 123 * m); // |123 m*****|\nstd::println(\"|{:*&gt;10}|\", 123 * m); // |*****123 m|\nstd::println(\"|{:*^10}|\", 123 * m); // |**123 m***|\n</code></pre> <p>Note</p> <p><code>std::println</code> is a C++23 facility. In case you do not have access to C++23, you can obtain the same output with:</p> <pre><code>std::cout &lt;&lt; std::format(\"&lt;format-string&gt;\\n\", &lt;format-args&gt;);\n</code></pre>"},{"location":"users_guide/framework_basics/text_output/#quantity-value-symbol-or-both","title":"Quantity value, symbol, or both?","text":"<p>The user can easily decide to either print a whole quantity (value and symbol) or only its parts. Also, a custom style of quantity formatting might be applied:</p> <pre><code>std::println(\"{:%Q}\", 123 * km); // 123\nstd::println(\"{:%q}\", 123 * km); // km\nstd::println(\"{:%Q%q}\", 123 * km); // 123km\n</code></pre>"},{"location":"users_guide/framework_basics/text_output/#quantity-value-formatting","title":"Quantity value formatting","text":"<p><code>sign</code> token allows us to specify how the value's sign is being printed:</p> <pre><code>std::println(\"{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}\", 1 * m); // 1 m,+1 m,1 m, 1 m\nstd::println(\"{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}\", -1 * m); // -1 m,-1 m,-1 m,-1 m\n</code></pre> <p>where:</p> <ul> <li><code>+</code> indicates that a sign should be used for both non-negative and negative numbers,</li> <li><code>-</code> indicates that a sign should be used for negative numbers and negative zero only (this is the default behavior),</li> <li><code>&lt;space&gt;</code> indicates that a leading space should be used for non-negative numbers other than negative zero, and a minus sign for negative numbers and negative zero.</li> </ul> <p><code>precision</code> token is allowed only for floating-point representation types:</p> <pre><code>std::println(\"{:%.0Q %q}\", 1.2345 * m); // 1 m\nstd::println(\"{:%.1Q %q}\", 1.2345 * m); // 1.2 m\nstd::println(\"{:%.2Q %q}\", 1.2345 * m); // 1.23 m\n</code></pre> <p><code>units-rep-type</code> specifies how a value of the representation type is being printed. For integral types:</p> <pre><code>std::println(\"{:%bQ %q}\", 42 * m); // 101010 m\nstd::println(\"{:%BQ %q}\", 42 * m); // 101010 m\nstd::println(\"{:%dQ %q}\", 42 * m); // 42 m\nstd::println(\"{:%oQ %q}\", 42 * m); // 52 m\nstd::println(\"{:%xQ %q}\", 42 * m); // 2a m\nstd::println(\"{:%XQ %q}\", 42 * m); // 2A m\n</code></pre> <p>The above can be printed in an alternate version thanks to the <code>#</code> token:</p> <pre><code>std::println(\"{:%#bQ %q}\", 42 * m); // 0b101010 m\nstd::println(\"{:%#BQ %q}\", 42 * m); // 0B101010 m\nstd::println(\"{:%#oQ %q}\", 42 * m); // 052 m\nstd::println(\"{:%#xQ %q}\", 42 * m); // 0x2a m\nstd::println(\"{:%#XQ %q}\", 42 * m); // 0X2A m\n</code></pre> <p>For floating-point values, the <code>units-rep-type</code> token works as follows:</p> <pre><code>std::println(\"{:%aQ %q}\", 1.2345678 * m); // 0x1.3c0ca2a5b1d5dp+0 m\nstd::println(\"{:%.3aQ %q}\", 1.2345678 * m); // 0x1.3c1p+0 m\nstd::println(\"{:%AQ %q}\", 1.2345678 * m); // 0X1.3C0CA2A5B1D5DP+0 m\nstd::println(\"{:%.3AQ %q}\", 1.2345678 * m); // 0X1.3C1P+0 m\nstd::println(\"{:%eQ %q}\", 1.2345678 * m); // 1.234568e+00 m\nstd::println(\"{:%.3eQ %q}\", 1.2345678 * m); // 1.235e+00 m\nstd::println(\"{:%EQ %q}\", 1.2345678 * m); // 1.234568E+00 m\nstd::println(\"{:%.3EQ %q}\", 1.2345678 * m); // 1.235E+00 m\nstd::println(\"{:%gQ %q}\", 1.2345678 * m); // 1.23457 m\nstd::println(\"{:%gQ %q}\", 1.2345678e8 * m); // 1.23457e+08 m\nstd::println(\"{:%.3gQ %q}\", 1.2345678 * m); // 1.23 m\nstd::println(\"{:%.3gQ %q}\", 1.2345678e8 * m); // 1.23e+08 m\nstd::println(\"{:%GQ %q}\", 1.2345678 * m); // 1.23457 m\nstd::println(\"{:%GQ %q}\", 1.2345678e8 * m); // 1.23457E+08 m\nstd::println(\"{:%.3GQ %q}\", 1.2345678 * m); // 1.23 m\nstd::println(\"{:%.3GQ %q}\", 1.2345678e8 * m); // 1.23E+08 m\n</code></pre>"},{"location":"users_guide/framework_basics/text_output/#unit-symbol-formatting","title":"Unit symbol formatting","text":"<p>Unit symbols of some quantities are specified to use Unicode signs by the SI (i.e. <code>\u03a9</code> symbol for the resistance quantity). The mp-units library follows this by default. From the engineering point of view, sometimes Unicode text might not be the best solution as terminals of many (especially embedded) devices are ASCII-only. In such a case, the unit symbol can be forced to be printed using ASCII-only characters thanks to <code>units-text-encoding</code> token:</p> <pre><code>std::println(\"{}\", 10 * si::ohm); // 10 \u03a9\nstd::println(\"{:%Q %Aq}\", 10 * si::ohm); // 10 ohm\nstd::println(\"{}\", 125 * us); // 125 \u00b5s\nstd::println(\"{:%Q %Aq}\", 125 * us); // 125 us\nstd::println(\"{}\", 9.8 * (m / s2)); // 9.8 m/s\u00b2\nstd::println(\"{:%Q %Aq}\", 9.8 * (m / s2)); // 9.8 m/s^2\n</code></pre> <p>Additionally, both ISQ and SI leave some freedom on how to print unit symbols. This is why two additional tokens were introduced.</p> <p><code>units-unit-symbol-solidus</code> specifies how the division of units should look like. By default, <code>/</code> will be used only when the denominator contains only one unit. However, with the <code>a</code> or <code>n</code> options, we can force the facility to print the <code>/</code> character always (even when there are more units in the denominator), or never in which case a parenthesis will be added to enclose all denominator units.</p> <pre><code>std::println(\"{:%Q %q}\", 1 * (m / s)); // 1 m/s\nstd::println(\"{:%Q %q}\", 1 * (kg / m / s2)); // 1 kg m\u207b\u00b9 s\u207b\u00b2\nstd::println(\"{:%Q %aq}\", 1 * (m / s)); // 1 m/s\nstd::println(\"{:%Q %aq}\", 1 * (kg / m / s2)); // 1 kg/(m s\u00b2)\nstd::println(\"{:%Q %nq}\", 1 * (m / s)); // 1 m s\u207b\u00b9\nstd::println(\"{:%Q %nq}\", 1 * (kg / m / s2)); // 1 kg m\u207b\u00b9 s\u207b\u00b2\n</code></pre> <p>Also, there are a few options to separate the units being multiplied:</p> <p>ISO 80000-1</p> <p>When symbols for quantities are combined in a product of two or more quantities, this combination is indicated in one of the following ways: <code>ab</code>, <code>a b</code>, <code>a \u00b7 b</code>, <code>a \u00d7 b</code></p> <p>NOTE 1 In some fields, e.g., vector algebra, distinction is made between <code>a \u2219 b</code> and <code>a \u00d7 b</code>.</p> <p>As of today, the mp-units library provides the support for <code>a b</code> and <code>a \u00b7 b</code> only. Additionally, we decided that the extraneous space in the latter case makes the result too verbose, so we decided to just use the <code>\u00b7</code> symbol as a separator.</p> <p>Note</p> <p>Please let us know in case you require more formatting options here.</p> <p>The <code>units-unit-symbol-separator</code> token allows us to obtain the following outputs:</p> <pre><code>std::println(\"{:%Q %q}\", 1 * (kg * m2 / s2)); // 1 kg m\u00b2/s\u00b2\nstd::println(\"{:%Q %dq}\", 1 * (kg * m2 / s2)); // 1 kg\u22c5m\u00b2/s\u00b2\n</code></pre>"},{"location":"users_guide/framework_basics/the_affine_space/","title":"The Affine Space","text":"<p>The affine space has two types of entities:</p> <ul> <li>point - a position specified with coordinate values (i.e. location, address, etc.)</li> <li>vector - the difference between two points (i.e. shift, offset, displacement, duration, etc.)</li> </ul> <p>Note</p> <p>The vector described here is specific to the affine space theory and is not the same thing as the quantity of a vector character that we discussed in the \"Scalars, vectors, and tensors\" chapter (although, in some cases, those terms may overlap).</p>"},{"location":"users_guide/framework_basics/the_affine_space/#operations-in-the-affine-space","title":"Operations in the affine space","text":"<p>Here are the primary operations one can do in the affine space:</p> <ul> <li>vector + vector -&gt; vector</li> <li>vector - vector -&gt; vector</li> <li>-vector -&gt; vector</li> <li>vector * scalar -&gt; vector</li> <li>scalar * vector -&gt; vector</li> <li>vector / scalar -&gt; vector</li> <li>point - point -&gt; vector</li> <li>point + vector -&gt; point</li> <li>vector + point -&gt; point</li> <li>point - vector -&gt; point</li> </ul> <p>Important</p> <p>It is not possible to:</p> <ul> <li>add two points,</li> <li>subtract a point from a vector,</li> <li>multiply nor divide points with anything else.</li> </ul>"},{"location":"users_guide/framework_basics/the_affine_space/#vector-is-modeled-by-quantity","title":"Vector is modeled by <code>quantity</code>","text":"<p>Up until now, each time when we used a <code>quantity</code> in our code, we were modeling some kind of a difference between two things:</p> <ul> <li>the distance between two points</li> <li>duration between two time points</li> <li>the difference in speed (even if relative to <code>0</code>)</li> </ul> <p>As we already know, a <code>quantity</code> type provides all operations required for vector type in the affine space.</p>"},{"location":"users_guide/framework_basics/the_affine_space/#point-is-modeled-by-pointorigin-and-quantity_point","title":"Point is modeled by <code>PointOrigin</code> and <code>quantity_point</code>","text":"<p>In the mp-units library the point abstraction is modelled by:</p> <ul> <li><code>PointOrigin</code> concept that specifies measurement origin,</li> <li><code>quantity_point</code> class template that specifies a point relative to a specific predefined origin.</li> </ul>"},{"location":"users_guide/framework_basics/the_affine_space/#absolute-point-origin","title":"Absolute point origin","text":"<p>The absolute point origin specifies where the \"zero\" of our measurement's scale is. User can specify such an origin by deriving from the <code>absolute_point_origin</code> class template:</p> <pre><code>constexpr struct mean_sea_level : absolute_point_origin&lt;isq::altitude&gt; {} mean_sea_level;\n</code></pre>"},{"location":"users_guide/framework_basics/the_affine_space/#quantity_point","title":"<code>quantity_point</code>","text":"<p>The <code>quantity_point</code> class template specifies an absolute quantity with respect to an origin:</p> <pre><code>template&lt;Reference auto R,\nPointOriginFor&lt;get_quantity_spec(R)&gt; auto PO,\nRepresentationOf&lt;get_quantity_spec(R).character&gt; Rep = double&gt;\nclass quantity_point;\n</code></pre> <p>As we can see above, the <code>quantity_point</code> class template exposes one additional parameter compared to <code>quantity</code>. The <code>PO</code> parameter satisfies a <code>PointOriginFor</code> concept and specifies the origin of our measurement scale.</p> <p>Tip</p> <p><code>quantity_point</code> definition can be found in the <code>mp-units/quantity_point.h</code> header file.</p> <p>As a point can be represented with a vector from the origin, a <code>quantity_point</code> class template can be created with the following operations:</p> <pre><code>quantity_point qp1 = mean_sea_level + 42 * m;\nquantity_point qp2 = 42 * m + mean_sea_level;\nquantity_point qp3 = mean_sea_level - 42 * m;\n</code></pre> <p>Note</p> <p>It is not allowed to subtract a point from a vector thus <code>42 * m - mean_sea_level</code> is an invalid operation.</p> <p>Similarly to creation of a quantity, if someone does not like the operator-based syntax to create a <code>quantity_point</code>, the same results can be achieved with <code>make_quantity_point</code> factory function:</p> <pre><code>quantity_point qp4 = make_quantity_point&lt;mean_sea_level&gt;(42 * m);\nquantity_point qp5 = make_quantity_point&lt;mean_sea_level&gt;(-42 * m);\n</code></pre> <p>The provided <code>quantity</code> representing an offset from the origin is stored inside the <code>quantity_point</code> class template and can be obtained with a <code>quantity_from_origin()</code> member function:</p> <pre><code>constexpr quantity_point everest_base_camp_alt = mean_sea_level + isq::altitude(5364 * m);\nstatic_assert(everest_base_camp_alt.quantity_from_origin() == 5364 * m);\n</code></pre>"},{"location":"users_guide/framework_basics/the_affine_space/#relative-point-origin","title":"Relative point origin","text":"<p>We often do not have only one ultimate \"zero\" point when we measure things.</p> <p>Continuing the Mount Everest trip example above, measuring all daily hikes from the <code>mean_sea_level</code> might not be efficient. Maybe we know that we are not good climbers, so all our climbs can be represented with an 8-bit integer type allowing us to save memory in our database of climbs? Why not use <code>everest_base_camp_alt</code> as our reference point?</p> <p>For this purpose, we can define a <code>relative_point_origin</code> in the following way:</p> <pre><code>constexpr struct everest_base_camp : relative_point_origin&lt;everest_base_camp_alt&gt; {} everest_base_camp;\n</code></pre> <p>The above can be used as an origin for subsequent points:</p> <pre><code>constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m);\nstatic_assert(first_climb_alt.quantity_from_origin() == 42 * m);\n</code></pre> <p>As we can see above, the <code>quantity_from_origin()</code> member function returns a relative distance from the current point origin. In case we would like to know the absolute altitude that we reached on this climb, we can subtract the absolute point origin from the current point:</p> <pre><code>static_assert(first_climb_alt - mean_sea_level == 5406 * m);\nstatic_assert(first_climb_alt - first_climb_alt.absolute_point_origin == 5406 * m);\n</code></pre>"},{"location":"users_guide/framework_basics/the_affine_space/#converting-between-different-representations-of-the-same-point","title":"Converting between different representations of the same point","text":"<p>As we might represent the same point with vectors from various origins, the mp-units library provides facilities to convert the point to the <code>quantity_point</code> class templates expressed in terms of different origins.</p> <p>For this purpose, we can either use:</p> <ul> <li> <p>a converting constructor:</p> <pre><code>constexpr quantity_point&lt;isq::altitude[m], mean_sea_level, int&gt; qp = first_climb_alt;\nstatic_assert(qp.quantity_from_origin() == 5406 * m);\n</code></pre> </li> <li> <p>a dedicated conversion interface:</p> <pre><code>constexpr quantity_point qp = first_climb_alt.point_for(mean_sea_level);\nstatic_assert(qp.quantity_from_origin() == 5406 * m);\n</code></pre> </li> </ul> <p>Note</p> <p>It is only allowed to convert between various origins defined in terms of the same <code>absolute_point_origin</code>. Even if it is possible to express the same point as a vector from another <code>absolute_point_origin</code>, the library will not provide such a conversion. A custom user-defined conversion function will be needed to add this functionality.</p> <p>Said otherwise, in the mp-units library, there is no way to spell how two distinct <code>absolute_point_origin</code> types relate to each other.</p>"},{"location":"users_guide/framework_basics/the_affine_space/#point-arithmetics","title":"Point arithmetics","text":"<p>Let's assume we will attend the CppCon conference hosted in Aurora, CO, and we want to estimate the distance we will travel. We have to take a taxi to a local airport, fly to DEN airport with a stopover in FRA, and, in the end, get a cab to the Gaylord Rockies Resort &amp; Convention Center:</p> <pre><code>constexpr struct home : absolute_point_origin&lt;isq::distance&gt; {} home;\nquantity_point&lt;isq::distance[km], home&gt; home_airport = home + 15 * km;\nquantity_point&lt;isq::distance[km], home&gt; fra_airport = home_airport + 829 * km;\nquantity_point&lt;isq::distance[km], home&gt; den_airport = fra_airport + 8115 * km;\nquantity_point&lt;isq::distance[km], home&gt; cppcon_venue = den_airport + 10.1 * mi;\n</code></pre> <p>As we can see above, we can easily get a new point by adding a quantity to an origin or another quantity point.</p> <p>If we want to find out the distance traveled between two points, we simply subtract them:</p> <pre><code>quantity&lt;isq::distance[km]&gt; total = cppcon_venue - home;\nquantity&lt;isq::distance[km]&gt; flight = den_airport - home_airport;\n</code></pre> <p>If we would like to find out the total distance traveled by taxi as well, we have to do a bit more calculations:</p> <pre><code>quantity&lt;isq::distance[km]&gt; taxi1 = home_airport - home;\nquantity&lt;isq::distance[km]&gt; taxi2 = cppcon_venue - den_airport;\nquantity&lt;isq::distance[km]&gt; taxi = taxi1 + taxi2;\n</code></pre> <p>Now, if we print the results:</p> <pre><code>std::cout &lt;&lt; \"Total distance: \" &lt;&lt; total &lt;&lt; \"\\n\";\nstd::cout &lt;&lt; \"Flight distance: \" &lt;&lt; flight &lt;&lt; \"\\n\";\nstd::cout &lt;&lt; \"Taxi distance: \" &lt;&lt; taxi &lt;&lt; \"\\n\";\n</code></pre> <p>we will see the following output:</p> <pre><code>Total distance: 8975.25 km\nFlight distance: 8944 km\nTaxi distance: 31.2544 km\n</code></pre> <p>Note</p> <p>It is not allowed to subtract two point origins defined in terms of <code>absolute_point_origin</code> (i.e. <code>mean_sea_level - mean_sea_level</code>) as those do not contain information about the unit so we are not able to determine a resulting <code>quantity</code> type.</p>"},{"location":"users_guide/framework_basics/the_affine_space/#temperature-support","title":"Temperature support","text":"<p>Another important example of relative point origins is support of temperature quantity points in units different than kelvin [<code>K</code>].</p> <p>The SI system definition in the mp-units library provides two predefined point origins:</p> <pre><code>namespace mp_units::si {\ninline constexpr struct absolute_zero : absolute_point_origin&lt;isq::thermodynamic_temperature&gt; {} absolute_zero;\ninline constexpr struct ice_point : relative_point_origin&lt;absolute_zero + 273.15 * kelvin&gt; {} ice_point;\n}\n</code></pre> <p>With the above, we can be explicit what is the origin of our temperature point. For example, if we want to implement the degree Celsius scale we can do it as follows:</p> <pre><code>using Celsius_point = quantity_point&lt;isq::Celsius_temperature[deg_C], si::ice_point&gt;;\n</code></pre> <p>Note</p> <p>Notice that while stacking point origins, we can use not only different representation types but also different units for an origin and a point. In the above example, the relative point origin is defined in terms of <code>si::kelvin</code>, while the quantity point uses <code>si::degree_Celsius</code>.</p> <p>To play a bit with temperatures we can implement a simple room's AC temperature controller in the following way:</p> <pre><code>constexpr struct room_reference_temp : relative_point_origin&lt;si::ice_point + 21 * deg_C&gt; {} room_reference_temp;\nusing room_temp = quantity_point&lt;isq::Celsius_temperature[deg_C], room_reference_temp&gt;;\nconstexpr auto step_delta = isq::Celsius_temperature(0.5 * deg_C);\nconstexpr int number_of_steps = 6;\nroom_temp room_low = room_reference_temp - number_of_steps * step_delta;\nroom_temp room_high = room_reference_temp + number_of_steps * step_delta;\nstd::println(\"| {:&lt;14} | {:^18} | {:^18} | {:^18} |\", \"Temperature\", \"Room reference\", \"Ice point\", \"Absolute zero\");\nstd::println(\"|{0:=^16}|{0:=^20}|{0:=^20}|{0:=^20}|\", \"\");\nauto print = [&amp;](std::string_view label, auto v){\nstd::println(\"| {:&lt;14} | {:^18} | {:^18} | {:^18} |\",\nlabel, v - room_reference_temp, v - si::ice_point, v - si::absolute_zero);\n};\nprint(\"Lowest\", room_low);\nprint(\"Default\", room_reference_temp);\nprint(\"Highest\", room_high);\n</code></pre> <p>The above prints:</p> <pre><code>| Temperature | Room reference | Ice point | Absolute zero |\n|================|====================|====================|====================|\n| Lowest | -3 \u00b0C | 18 \u00b0C | 291.15 \u00b0C |\n| Default | 0 \u00b0C | 21 \u00b0C | 294.15 \u00b0C |\n| Highest | 3 \u00b0C | 24 \u00b0C | 297.15 \u00b0C |\n</code></pre>"},{"location":"users_guide/framework_basics/the_affine_space/#no-text-output-for-points","title":"No text output for points","text":"<p>The library does not provide a text output for quantity points, as printing just a number and a unit is not enough to adequately describe a quantity point. Often, an additional postfix is required.</p> <p>For example, the text output of <code>42 m</code> may mean many things and can also be confused with an output of a regular quantity. On the other hand, printing <code>42 m AMSL</code> for altitudes above mean sea level is a much better solution, but the library does not have enough information to print it that way by itself.</p>"},{"location":"users_guide/framework_basics/the_affine_space/#the-affine-space-is-about-type-safety","title":"The affine space is about type-safety","text":"<p>The following operations are not allowed in the affine space:</p> <ul> <li>adding two <code>quantity_point</code> objects<ul> <li>It is physically impossible to add positions of home and Denver airports.</li> </ul> </li> <li>subtracting a <code>quantity_point</code> from a <code>quantity</code><ul> <li>What would it mean to subtract DEN airport location from the distance to it?</li> </ul> </li> <li>multiplying/dividing a <code>quantity_point</code> with a scalar<ul> <li>What is the position of <code>2 *</code> DEN airport location?</li> </ul> </li> <li>multiplying/dividing a <code>quantity_point</code> with a quantity<ul> <li>What would multiplying the distance with the DEN airport location mean?</li> </ul> </li> <li>multiplying/dividing two <code>quantity_point</code> objects<ul> <li>What would multiplying home and DEN airport location mean?</li> </ul> </li> <li>mixing <code>quantity_points</code> of different quantity kinds<ul> <li>It is physically impossible to subtract time from length.</li> </ul> </li> <li>mixing <code>quantity_points</code> of inconvertible quantities<ul> <li>What does it mean to subtract a distance point to DEN airport from the Mount Everest base camp altitude?</li> </ul> </li> <li>mixing <code>quantity_points</code> of convertible quantities but with unrelated origins<ul> <li>How to subtract a point on our trip to CppCon measured relatively to our home location from a point measured relative to the center of the Solar System?</li> </ul> </li> </ul> <p>Important</p> <p>The usage of <code>quantity_point</code> and affine space types in general, improves expressiveness and type-safety of the code we write.</p>"},{"location":"users_guide/framework_basics/value_conversions/","title":"Value Conversions","text":""},{"location":"users_guide/framework_basics/value_conversions/#value-preserving-conversions","title":"Value-preserving conversions","text":"<pre><code>auto q1 = 5 * km;\nstd::cout &lt;&lt; q1.in(m) &lt;&lt; '\\n';\nquantity&lt;si::metre, int&gt; q2 = q1;\n</code></pre> <p>The second line above converts the current quantity to the one expressed in metres and prints its contents. The third line converts the quantity expressed in kilometres into the one measured in metres.</p> <p>Important</p> <p>It is always assumed that one can convert a quantity into another one with a unit of a higher resolution. There is no protection against overflow of the representation type. In case the target quantity ends up with a value bigger than the representation type can handle, you will be facing Undefined Behavior.</p> <p>In case a user would like to perform an opposite transformation:</p> <pre><code>auto q1 = 5 * m;\nstd::cout &lt;&lt; q1.in(km) &lt;&lt; '\\n';\nquantity&lt;si::kilo&lt;si::metre&gt;, int&gt; q2 = q1;\n</code></pre> <p>Both conversions will fail to compile.</p> <p>There are two ways to make the above work. The first solution is to use a floating-point representation type:</p> <pre><code>auto q1 = 5. * m;\nstd::cout &lt;&lt; q1.in(km) &lt;&lt; '\\n';\nquantity&lt;si::kilo&lt;si::metre&gt;&gt; q2 = q1;\n</code></pre> <p>or</p> <pre><code>auto q1 = 5 * m;\nstd::cout &lt;&lt; value_cast&lt;double&gt;(q1).in(km) &lt;&lt; '\\n';\nquantity&lt;si::kilo&lt;si::metre&gt;&gt; q2 = q1; // double by default\n</code></pre> <p>Important</p> <p>The mp-units library follows <code>std::chrono::duration</code> logic and treats floating-point types as value-preserving.</p>"},{"location":"users_guide/framework_basics/value_conversions/#value-truncating-conversions","title":"Value-truncating conversions","text":"<p>The second solution is to force a truncating conversion:</p> <pre><code>auto q1 = 5 * m;\nstd::cout &lt;&lt; value_cast&lt;km&gt;(q1) &lt;&lt; '\\n';\nquantity&lt;si::kilo&lt;si::metre&gt;, int&gt; q2 = q1.force_in(km);\n</code></pre> <p>This explicit cast makes it clear that something unsafe is going on. It is easy to spot in code reviews or while chasing a bug in the source code.</p> <p>Note</p> <p><code>q.force_in(U)</code> is just a shortcut to run <code>value_cast&lt;U&gt;(q)</code>. There is no difference in behavior between those two interfaces. <code>q.force_in(U)</code> was added for consistency with <code>q.in(U)</code> and <code>q.force_numerical_value_in(U)</code>.</p> <p>Another place where this cast is useful is when a user wants to convert a quantity with a floating-point representation to the one using an integral one. Again this is a truncating conversion, so an explicit cast is needed:</p> <pre><code>quantity&lt;si::metre, int&gt; q3 = value_cast&lt;int&gt;(3.14 * m);\n</code></pre> <p>Info</p> <p>It is often fine to use an integral as a representation type, but in general, floating-point types provide better precision and are privileged in the library as they are considered to be value-preserving.</p>"},{"location":"users_guide/examples/tags_index/","title":"Tags Index","text":"<p>Tip</p> <p>All usage examples in this chapter are categorized with appropriate tags to simplify navigation and search of relevant code. You can either read all the examples one-by-one in the order provided by the documentation authors or, thanks to the tagging system, jump straight to the example that is the most interesting for you.</p>"},{"location":"users_guide/examples/tags_index/#cgs-system","title":"CGS System","text":"<ul> <li>avg_speed</li> </ul>"},{"location":"users_guide/examples/tags_index/#international-system","title":"International System","text":"<ul> <li>avg_speed</li> <li>hello_units</li> </ul>"},{"location":"users_guide/examples/tags_index/#physical-constants","title":"Physical Constants","text":"<ul> <li>si_constants</li> </ul>"},{"location":"users_guide/examples/tags_index/#text-formatting","title":"Text Formatting","text":"<ul> <li>avg_speed</li> <li>hello_units</li> <li>si_constants</li> </ul>"}]}