**C++ Units
**by Calum Grant

Units home page: http://calumgrant.net/units

**Introduction**

This library is for engineers and scientists who deal with physical quantities. Units provide a safety net that check the validity of formulae at compile-time and ensure that different units are converted where necessary. A large number of physical quantities are provided by the library, including all SI units.

This library is for software engineers. By giving a quantity a unit, it prevents the wrong value being assigned to the wrong thing, or passing the wrong value to the wrong argument of a function. Units documentation a program -

intis not descriptive, butapplesis.The library uses templates and generative programming techniques to handle units at compile time, so there is no run-time overhead. Many units are predefined and it is straightforward to add your own.

The library is installed by copying the file units.hpp into your include directory. To use the library

#include "units.hpp"

**Using built in units**

The built in physical quantities are in the namespace

units::values, so for the purposes of this tutorial, assume thatusing namespace units::values;has been written somewhere near the top of the file. This namespace gives you access to the following physical quantities:

- No unit:
unit, percent, dozen, bakers_dozen- Mass:
kg, g, mg, lb, oz, tonne- Time:
s, ms, minute, hour, day, week- Calendar:
month, year, century, millennium- Distance:
m, cm, mm, km, inch, foot, yard, mile, nautical_mile- Temperature:
K, Celsius, Fahrenheit- Force:
N- Pressure:
Pa, kPa, psi, millibar- Energy:
J- Power:
W- Area:
m2, hectare, are, inch2, acre- Volume:
cm3, ml, cl, liter, dl, m3- Velocity:
mph, kph, meters_per_second, knot, mach- Angle:
rad, degree, grad, degree_minute, degree_second- Other units:
A, mol, cd, rad, sr, Hz, C, V, F, Ohm, S, Wb, T, H, lm, lx, Bq, Gy, Sv, kat, rpmThe naming convention is to use the symbol of the SI unit. For other units, the name is used since there are no standard symbols.

The built-in values are all doubles, however you can change this (see next section). The physical quantities behave exactly as normal doubles, except that they check the units and perform conversion where necessary.

A value can be constructed from a number, another value, or the default constructor which initializes the value to zero. When constructing from another value, the other value must be compatible or you will get a compile-time error:

m x; // x = 0 m y(10); // y = 10 m z(cm(250)); // z = 2.5 m w(s(3)); // Compile-time error: incompatible units kph(mph(70)) // Convert miles per hour to km per hourAssignment is only possible from another value, and the value must be compatible:

x = 10; // Compile-time error: 10 of what? x = cm(200); // Ok: x = 2.0 x = s(3) // Compile-time error: incompatible unitsThe number in the value can be obtained using the

get()method:x.get(); // Get current value of x cm(m(5)).get() // 500The normal arithmetic operators are supported:

+, -, *, /, /=, *=, ++, --. There are some restrictions however. You can only add or subtract compatible values. You can multiply or divide by any other value, which returns a value with a unit with the correct type (multiplied or divided). The*=and/=operators can only take a number (in general if you multiply by another unit the type will be different). e.g.miles x = m(1) + cm(10); // Add two distances m2 area = foot(10) * yard(3); // Converts to meters squared s timer; timer += 10; // Compile-time error: add 10 of what timer += s(10); // Ok: add 10 seconds to timer liter v = hectare(1) * mm(1); // Ok H h( m(4)*m(8)*kg(2)/s(2)/s(4)/A(1)/A(2) ); // A complex (but valid) formula s(10) + m(4); // Compile-time error: Can't add time and distanceThe comparison operators (

==, !=, <, <=, >, >=) also convert between units implicitly:minutes(2) > s(70) // trueWriting a value to a stream (using

operator <<) displays the unit after the value. Many of the built in units have names, otherwise the text is generated, as in the following example:std::cout << "Flow rate is " << m3(mile(1)*inch(80)*foot(9))/s(minute(5)); // Output: Flow rate is 29.9026 (m)^3.(s)^-1The

units::sqrtfunction provides a square root - which also takes the root of the unit of course.m a, b, c; c = units::sqrt( a*a + b*b );You can take an arbitrary rational power of a number using the

units::raisetemplate, however you must specify the power at compile time so that the compiler knows the unit of the return value. e.g.units::raise<3,1>(m(2)) == m(2)*m(2)*m(2)There are some trigonometric functions (

units::sin,units::cosandunits::tan) which take an angle. You can supply any unit of angle to the function and the function will convert to radians. e.g.units::tan( degree(45) );There are a number of constants available in the

units::constantsnamespace. These are

k, mu, NA, G0, e0, me, eV, e, F, alpha, inv_alpha, u0, phi0, R, G, h, h_bar, mp, Rinf, c, rho, pi, lightyear, gExample:

N attractive_force = units::constants::G * kg(1) * kg(2) / m(3) / m(5);

**Creating new units**

Values with units are provided by theunits::value<>class template, declared as follows:template<typename Value, typename Unit> class value;The predefined units (as used in the previous section) are declared in the

units::unitsnamespace. So you can reuse any of these units but provide a different type, e.g.units::value<float, units::units::m> length;The unit can be any type, so to create a new unit, just create a new type:

struct apples; struct oranges; typedef units::value<int, apples> apples_t; typedef units::value<int, oranges> oranges_t;The new value will be protected from interoperating with naked numbers or other types of unit:apples_t n(5); n = oranges_t(3); // Compile-time error n = apples_t(10); // Ok

**Displaying units **

When a value is output (using

operator<<), it appends the unit to the stream. By default the unit will display the text"units". You can change this by declaring the name of the unit with theUNITS_DISPLAY_NAMEmacro, which takes the unit as its first parameter, and its name as its second parameter. e.g.UNITS_DISPLAY_NAME( apples, "apples" ); UNITS_DISPLAY_NAME( oranges, "oranges" );std::cout << oranges_t(2); // Output: 2 oranges

**Converting between units **

A unit can be defined in terms of other units. The templates

units::scale, units::translate, units::powandunits::composeprovide a means of constructing new units which can be converted from and to another unit.The

units::scale<>template constructs a unit which is a multiple of another:struct penny; typedef units::scale<penny, 4> farthing; // Multiply penny * 4 to get farthings typedef units::scale<penny, 1, 12> shilling; // Multiply penny by 1/12 to get shillings typedef units::scale<shilling, 1, 20> pound; typedef units::scale<penny, 1, 30> half_crown; typedef units::scale<half_crown, 1, 2> crown;std::cout << "There are " << penny(pound(1)).get() << " old pence in the pound\n";The

units::translate<>template constructs a unit which is offset from another:typedef units::translate<units::units::K, -27315, 100> Celcius; // Celcius = K - 27315/100The

units::pow<>template constructs a unit which is a power of another unit:typedef units::pow<units::units::m, 3> m3; // One cubic meterFinally the

units::compose<>template creates a unit which multiplies two other units:typedef units::compose<power, time> energy; typedef units::compose< units::units::m, units::pow<units::units::s, -1> > meters_per_second;The conversion operators are able to analyse the type of the unit and generate a conversion function automatically!