This is the second release of this library - details of changes made can be found in the** **History section at the end. There are some fixes, a covenient way of defining a unit as the inverse of another** **has been provided and it now correctly handles datum based measurements such as Celcius or height above a datum.

## Contents

#Introduction, #Background, #Features, #Datum Measurements, #Quick reference,

#Encapsulation of dimensional analysis, #Architecture of a unit type,

#Code changes in the second release, #Final comments, #History

## Introduction

This article could be of interest to anyone looking for a self contained and usable units of measurement library but is also an illustration of how templates can be used to extend the work of the compiler in checking and interpreting the code you write without generating any extra run time code.

It is not opaque template meta programming but it makes light use of some of the techniques on which template meta programming is built. It is lightweight in that it generates no extra run time code but also in that it overburdens neither the compiler nor a programmer trying to understand how it works. I did not approach this with a mastery of template techniques, instead I have developed a particular working knowledge of them in order to solve particular problems. I believe this 'engineering' approach may be a good match for the world that many programmers live and think in.

It does not require any of the features extended by more recent versions of C++. `constexpr`

would be usefull in this context but it remains far from universally available.

If you just want to use this library to get things done then go directly to the #Quick reference section and use the #Features section as a further reference.

## Background

A variety of units of measurement libraries already exist but for some reason their use doesn't seem to be popular. To be honest I have only given them a quick glance because I don't really need a units library. I just wanted to write one, motivated by an interest in using compile time template techniques to produce richer more intelligent data types. Of course what I have written is the one that I would like to use should I need one but also with an eye on its potential use by others.

As with many units systems it has at its heart Barton’s and Nackman’s approach to dimensional analysis described by Scott Meyers at

http://se.ethz.ch/~meyer/publications/OTHERS/scott_meyers/dimensions.pdf

This is based on template parameters representing basic dimensions such as Length, Mass and Time.

template<int Length, int Mass, int Time> class unit
{
double value;
....
};

so

typedef unit<1, 0, 0> LENGTH;
typedef unit<0, 1, 0> MASS;
typedef unit<0, 0, 1> TIME;

and

typedef unit<1, 0, -1> VELOCITY; typedef unit<1, 0, -2> ACCERATION; typedef unit<1, 1, -2> FORCE;

This makes it possible to check that assignment, comparison, addition and subtraction can only take place between units that have the same dimensions and that multiplication and division produce units of the correct type.

The most basic implementation of this approach has an elegant simplicity:

LENGTH metres(1);
LENGTH Kms(1000);
LENGTH m = 5*metres; LENGTH k = 2*Kms;

but it is slightly odd in its use of dimensions (e.g. LENGTH) as the data type (with implicit unstated units of metres) and variables as unit sizes. It is not directly units of measurement as the data type. Most units of measurement libraries provide a more sophisticated wrapping of this approach to overcome this and other limitations.

The following describes the features that have been built into this library.

## Features

First of all to illustrate that this system doesn't suck you into any more complexity than your situation demands, if your only requirement is to work with length, area and volume then your units definition could be as simple as this:

ulib_Dimension(1, LENGTH)
ulib_base_Unit(LENGTH, **metres**)

and this will enable you to write in your executable code:

**metres** length=3;
**metres** width=4;
**metres** height=5;
**sq_metres** area= length* width;
**cubic_metres** volume= area * height;

You may also wish to use other units of length which you can define as scalars of the metres you have defined:

ulib_scaled_Unit(**Kms**, =, 1000 , metres)
ulib_scaled_Unit(**cms**, =, 1/100 , metres)
ulib_scaled_Unit(**feet**, =, 0.3048 , metres)

and then in your executable code you can write;

**metres** m=1500;
**Kms** k=m; **double** ratio=k/m;

The scaling between `metres`

and `Kms`

is automatic and implicit and follows the rule of conserving quantity. That is, if a variable declared as `metres`

holding a value of 1500 is assigned directly to a variable declared as `Kms`

then the `Kms `

variable will be set to 1.5 (the same quantity). Conversion factors no longer need to appear in your code or even any indication that a conversion should be done.

**Defining units**

The work of defining units is carried out entirely with the macros that follow.

First of all Up to 7 dimensions can be named and used. There is a well established custom of using three and making them Length, Mass and Time. Others are possible but not so popular.

ulib_Dimension(1, LENGTH)
ulib_Dimension(2, MASS)
ulib_Dimension(3, TIME)

these named dimensions can then be used to define base units. In this case we will use the MKS base units of metres, kilograms and seconds:

ulib_base_Unit( LENGTH, **metres**)
ulib_base_Unit( MASS , **Kgs**)
ulib_base_Unit( TIME , **secs**)

we can add units scaled from these as required

ulib_scaled_Unit( **Kms**, =, 1000 , metres)
ulib_scaled_Unit( **mins**, =, 60 , secs)
ulib_scaled_Unit( **hours**, =, 60 , mins)

and we can build up a range of compoud units derived from binary combinations of existing units by multiplication or division. Here are some common unscaled compound units

ulib_compound_Unit( **metres_per_sec**, =, metres, Div, secs) ulib_compound_Unit( **metres_per_sec2**, =, metres_per_sec, Div, secs) ulib_compound_Unit( **Newtons**, =, metres_per_sec2, Mult, Kgs) ulib_compound_Unit( **Joules**, =, Newtons, Mult, metres) ulib_compound_Unit( **Watts**, =, Joules, Div, secs)

we can also create compound units from scaled units

ulib_compound_Unit( **Kms_phour**, =, Kms, Div, hours)

and scaled units from compound units

ulib_scaled_Unit( **Kg_force**, =, 9.806649999980076 , Newtons)

We can also define a unit as the inverse of another unit (added in 2nd release)

ulib_Unit_as_inverse_of( **Herz**, secs)

**Declaration**

Once you have defined a unit, (say `metres`

) you can declare variables using its name

**metres** length;

its built in square and cubic forms

**sq_metres **area;
**cubic_metres** volume;

its inverse

**inverse_of**<**metres**>**::type** closeness;

or raised to any integral power

**to_power<metres, 4>::type** variance_of_area;

The `::type`

ending is an appropriate reminder that `inverse_of`

and `to_power`

are type modifiers for unit declarations (a new concept that requires a new expression). They do not perform any operation on numbers.. They do not do anything at run tme.

In all cases the operations available between and on units are:

**Construction**

metres m=**metres**(1500);

- from unit with same dimensions

metres m=**Kms**(1.5);

metres m=**1500**;

**Assignment **`= , `

Addition and subtraction `+ - += -=`

, Less than and greater than comparision `< >, `

metres m **=** **metres**(1500);
m **+=** **metres**(50);
if( m **>** **metres**(1350);
;

- by unit with same dimensions

metres m **=** **Kms**(1.5);
m **+=** **Kms**(0.050);
if( m **>** **Kms**(1.35);
;

**Multiplication and division **`* /`

metres_psec V = **metres**(500) **/ secs**(10);
Joules Energy= **Newtons**(5) ***** **metres**(10);

metres m = **2***metres(50)***5**;
metres m = metres(50) **/ 5**;
inverse_of<metres>::type closeness= **5 /** metres(50) ;

**Extraction of value as a double** variable.**a****s**<unit_type>() or **as**<unit_type>(variable)

There is protection against inadvertant assignment of a unit variable with a numerical value in the wrong units and also against mistaking its units when reading its numerical value. The protection offered is line of sight. You can only assign a unit variable with a numerical value at the point of declaration where you can see the unit type you are dealing with.

**metres** m = **5**; .........
m = **15**; m = **metres**(15);

and you can only extract the numerical value (type double) by using the variable.**a****s**<unit_type>() dot method or the **as**<unit_type>(variable)

free function which require you to expicitly state the units in which you require the variable.

double dist = m: double dist_in_metres = m.**as<metres>**(); double dist_in_kilometres = **as<Kms>**(m); double mass_in_kilogrammes = **as<Kgs>**(m);

**Return unit variable representing p****ositive integer root**

variable.**integer_root**<root_function, positive integer>()

or **integer_root**<root_function, positive integer>(

variable)

Returns a unit variable that is in the correct units for an integer root. Will only compile is the source unit type is a power which is a mltiple of the same integer.

Requires function that performs the numerical root (takes a double, returns a double) to be passed as a template parameter

sq_metres area = 1600;
metres side_of_square = area.**integer_root**<sqrt, 2>();

**Test for aproximately equal according to given tolerance**

`variable1.`**is_approx**<tolerance_unit, numerator, denominator>(variable2)

.

Operator `==`

(exactly equal) is not practically useful with measured quantities so it is not implemented. Instead a dot method testing for aproximately equal according to given tolerance is provided. The template arguments specify the units, numerator and denominator of the tolerace. The following are equivalent:

if(m.**is_****approx<metres, 1, 100>**(metres(1550))
;
if(m.**is_****approx<cms, 1, 1>**(metres(1550))
;
if(m.**is_****approx<mms, 10, 1>**(Kms(1.55))
;

**Division with remainder **- `int n=variable1.`**goes_into**(variable2, Remainder)

This can be what is practically required in may circumstance. Units must match correctly including the Remainder variable passed in by reference to be filled.

**Operators **`++`

and --** are not implemented** because they have no meaning for a pure quantity. Their interpretation depends on the units chosen to measure that quantity. If you want their functionality you will have to write...

metres m=5:
m**+=metres(1)**; m**-=metres(1)**;

...which forces you to be explicit about the units involved in the change you are making to the quantity

The system has been designed to support expressions composed from these operations of any complexity.

**The following global functions have been depreciated from the library for the reasons stated**

- Square root returning variable of appropriate unit type -
** ****Sqrt**(variable)

Being a specific implementation of an existing function, `integer_root<sqrt, 2>(variable)`

, that draws in an outside dependancy ( sqrt from math.h) it does not properly belong to the library and is depreciated to an external option.

`<span style="font-size: 14.2857141494751px;">`**approx_equal**<tolerance_unit, numerator, denominator>(variable1, variable2)</span>

- the global equivalent of the **is_approx** method **how_many_in**(variable1, variable2, Remainder)

- the global equivalent of the **goes_into** method` `

These global functions are generic beyond the scope of the library in that they will work correctly with just about any numerical data type. This can useful but that same quality combined with their unmangled names carries a risk of clashing with other libraries. As such they cannot be implicit in the library and are depreciated to an external option.

These depreciated functions an be individually reactivated using the `ulib_using_depreciated(function_name)`

macro

ulib_using_depreciated(Sqrt)
ulib_using_depreciated(approx_equal)

**Multiple factorless base unit systems (for example MKS and cgs)**

If you have defined your base units as MKS (metres, Kilograms and seconds) and have defined centimetres as 1/100 metres and grams as 1/1000 Kilograms then any part of your code that is expressed in cgs units will calculate correctly and scale seamlessly. However it is annoying that there will be a hidden conversion to MKS units and back again for all intermediate calculations. For this reason this library will simultaneously respect more than one factorless base unit system (for example MKS and cgs) so that intermediate calculations can be factor free when dealing with metres kilogrammes and seconds and also when dealing with centimetres, grams and seconds. This is complicated by units (such as seconds in this case) that are common across factorless base unit systems but this problem has been solved for cases where the common units are common to all base systems in use (in practice it is quite difficult to contrive a situation where they would not be).

ulib_Dimension(1, LENGTH)
ulib_Dimension(2, MASS)
ulib_Dimension(3, TIME)
ulib_multiple_Bases(**2**)
ulib_all_bases_Unit(TIME, **secs**)
ulib_base1_Unit(LENGTH, **metres**)
ulib_base1_Unit(MASS, **Kgs**)
ulib_base2_Unit(LENGTH, **cms**, =, 0.01 , metres)
ulib_base2_Unit(MASS, **grams**, =, 0.001 , Kgs)

The MKS compound units (metres_per_sec etc.) can be now defined as before but units native to or with affinity to the cgs system can now be defined in relation to its base units:

ulib_compound_Unit( **cms_per_sec**, =, cms, Div, secs) ulib_compound_Unit( **cms_per_sec2**, =, cms_per_sec, Div, secs) ulib_compound_Unit( **Dynes**, =, cms_per_sec2, Mult, grams) ulib_compound_Unit( **Ergs**, =, Dynes, Mult, cms)

Now you can declare diverse units and mix them in complex expressions. It will recognise which units are derived from which base system and adjust its working base system according to which predominates in each sub-expression:

Newtons sum_forces = Newtons(4)+Kgs(1)*metres(2)/sq_secs(4)+Dynes(50)+grams(500)*cms(20)/sq_secs(3) ;

It will use MKS as its working base for the first part of this expression and cgs for the second part, performing all necessary conversions between the two. Remember, these decisions are made at compile time before you even execute the code. There is no dithering on ifs and else's at run time.

**More dimensions and more base unit systems**

Here is example that adds FPS(Feet, Pounds, seconds) to make three base unit systems and also a fourth dimension of ANGLE with units of radians across all three base unit systems. The dimension of ANGLE is used to qualify rotating systems so that torque does not have the same dimensions as energy. This is non standard (angle is considered to be a ratio of two lengths and therefore a dissolution of dimension) but if you put that ideological objection aside it works well and prevents confusion when you are working with rotating systems, as much engineering does.

The library has been coded to accept up to 7 dimensions and simultaneously respect up to 5 base unit systems.

ulib_Dimension(1, LENGTH)
ulib_Dimension(2, MASS)
ulib_Dimension(3, TIME)
ulib_Dimension(4, **ANGLE**)
ulib_multiple_Bases(**3**)
ulib_all_bases_Unit(TIME, **secs**)
ulib_all_bases_Unit(ANGLE, **radians**)
ulib_base1_Unit(LENGTH, **metres**)
ulib_base1_Unit(MASS, **Kgs**)
ulib_base2_Unit(LENGTH, **cms**, =, 0.01 , metres)
ulib_base2_Unit(MASS, **grams**, =, 0.001 , Kgs)
ulib_base3_Unit(LENGTH, **feet**, =, 0.3048 , metres)
ulib_base3_Unit(MASS, **Pounds**, =, 0.45359 , Kgs)

'Foot, Pound, Second' scaled and compound units can be built up in the same way as for MKS and cgs except that some unit names such as FPS_Force will need to be made up to represent unscaled compound units that don't have familiar names.

ulib_compound_Unit( **feet_per_sec**, =, feet, Div, secs) ulib_compound_Unit( **feet_per_sec2**, =, feet_per_sec, Div, secs) ulib_compound_Unit( **FPS_Force**, =, feet_per_sec2, Mult, Pounds) ulib_compound_Unit( **FPS_Energy**, =, FPS_Force, Mult, feet)
ulib_scaled_Unit( **Pounds_Force**, =, 3.217404867821228, FPS_Force) ulib_compound_Unit( **Feet_Pounds_Force**, =, PoundsForce, Mult, feet)

**Using ANGLE to break the apparent identity between Torque and Energy**

We will use the more rational MKS system to look at how adding a dimension ANGLE with units of radians resolves the confusion (and scope for error) between energy and torque. The confusion arises because energy is defined as a the action of a force moving something over a distance in the direction in which it acts and torque is defined as the turning effect of a force applied at a distance from the center of rotation. The two distances play a different role. In the first it lies along the line of the force and is the result of it. In the second it is perpendicular to the force and determines where it acts. We need metres radius to be dimensionally distict from metres in the line of action.

To resolve thus, let us look at energy in a rotating system. In a rotating system, there is only work done (energy) if torque causes it to rotate through an angle.

Joules= Nm_torque * radians.

If we make ANGLE a dimesion with units of radians then we will get a dimensional distinction between Torque and Energy.

So rearranging the equation above:

Nm_torque = Joules/ radians .

Now if we call our radial distance **radial_metres **(name it and then find out what it is) then

Nm_torque = Newtons* radial_metres

equating Torque from the two equations above:

Newtons* radial_metres = Joules/ radians

and divide both sides by Newtons

**radial_metres = metres / radians**

This is the key. We use radial_metres to describe radial distances on rotating systems and we define it as a compound of metres divided by radians:

ulib_compound_Unit( **radial_metres**, =, metres, Div, radians)

and then we can properly define Torque

ulib_compound_Unit( **Nm_torque**, =, Newtons, Mult, radial_metres)

Torque is now dimensionally distict from energy and has a coherent relationship with it.

Joules WorkDone = Nm_torque (200)*radians(6.28);

This also provides a coherent relationship between angular displacement and the length of arc it makes at a radius.

metres arclength = radial_metres(5)*radians(3.14);

**Optimising scaling between units**

Scaling between units is carried out using the scaling ratios that you have supplied. The manner of defining units ensures that it will always be possible to scale automatically between any two defined units but in some cases it may be carried out by a combination of those scaling ratios each time it is done. This will be the case between two units that are not base units and are not already defined as a ratio of each or are derived from different base systems. This is due to a limitation in how tradional compilers arrange the initialisation of const doubles.

Any of these more distant unit to unit scaling relationships may be collapsed to a single precalculated multiplication simply by providing an explicit relationship saying that it should be done.

**ulib_Precalc_unit_to_unit**(Kms , mms)
Kms dist = mms(2500);

The pre-calculation will not be carried out at compile time but it will be done once only at load time.

## Datum based measurements

This section is a response to one of the comments this article recieved after its first publication pointing out that there is a need to provide an intercept besides a multiplication factor with coversion between some measurements such as from Celsius to Fahrenheit. What must be understood is that:

**Celcius** is measurement that uses **degrees centigrade** as its units.

There is no problem with defining **degrees centigrade** and **degrees Fahrenheit** as units in the normal way to represent **temperature difference** in the same way that we use seconds to represent time interval. Conversion between temperature differences requires only the application of a factor as is provided for.

ulib_Dimension(5,**TEMPERATURE**)
ulib_base_Unit(TEMPERATURE, **degrees_C**)
ulib_scaled_Unit(**degrees_F**, =, **0.5555555**, degrees_C)

They can also be used to form compound units ....

ulib_compound_Unit( **degrees_C_per_metre**, =, degrees_C, Div, **metres**)
ulib_compound_Unit( **degrees_C_psec**, =, degrees_C, Div, **secs**)

The problem with **Celsius** and **temperature in Fahrenheit** is that they are not properly measures of quantity or amount. They are points on reference scales each using a different physical datum as zero. This has two consequences:

- That they require an offset to be applied when converting from one to another means that they must be declared as distinct from degrees centigrade and degrees Fahrenheit of temperature difference which only require the application of a factor during conversion.
- That zero does not represent non-existence (no effect) means that it hardly ever makes sense to use them directly as factors in mathematical formulae. To illustrate the absurdity of doing so; whole terms would zero out just because you happen to hit the arbitrary zero datum value and would do so differently depending on if you used Celsius or Fahrenheit.

Nevertheless temperatures are the measurements you take and Celcius and Fahrenheit are common reference scales. Many formulae recieve temperatures as their inputs and calculate the temperature differences that they work with internally. So there is a real practical need to find a coherent way of embracing them.

This is it:

ulib_datum_Measurement( **Celcius**, degrees_C, 0)
ulib_datum_Measurement( **Fahrenheit**, degrees_F, 32)

A datum measurement must be based on an existing unit and must specify its value at a physical datum common for all datum measurements of that dimension (in this case the freezing point of water).

Datum measurements are necessarily limited compared to normal quantity units. They are intended to store datum based measurements and correctly convert between measurements based on different datums and scales e.g. Celcius and Fahrenheit...

**Celcius** temperature_C1**=30;**
**Fahrenheit** temperature_F1**=temperature_C1;**

...to be modifiable by adding or subtracting quantity units of the same dimensions:

temperature_C1** += degrees_C(10);
**

...and to yeild a quantity unit as a result of the difference between two datum measurements...

**Celcius** temperature_C2=50;
**degrees_C **temperature_difference_C** = C2 - C1;**
temperature_difference_C = C2 - **F1**;
**degreesF** temperature_difference_F = C2 - F1;

Datum measurement types cannot be used to define compound units and datum measurements cannot be directly used as factors in mathematical formulae. There is also no conversion between a datum measurement and the quanitity unit on which it is based other than by differences as described above.

The one exception that is not restrained by these limitations is an absolute datum measurement:

ulib_absolute_datum_Measurement(**Kelvin**, degrees_C, **273**)

**Kelvin** is an expression of temperature in the same way as Celcius and Fahrenheit but with a different datum. Therefore it must be defined as a datum measurement, especially if we want correct automatic conversion with Celcius and Fahrenheit. However its zero datum of absolute zero really does represent non-existence therefore its numerical value really does represent a true quantity and is used as such in mainstream thermodynamics. Accordingly an absolute datum measurement can be be used to form compound units...

ulib_compound_Unit( **Kelvin_Kgs**, =, Kgs, Mult, Kelvin)

...and absolute datum measurement variables can be directly used as factors in mathematical formulae if they are postfixed with **()**

kelvin abs_temp=200;
Kgs mass=5;
Kelvin_Kgs KKgs =abs_temp**()** * mass;

The new datum measurement types have been developed with some thought to their application in other dimensions. The library should embrace what it needs to in a generic manner. I can think of two other examples:

**Date and Time** - When we define seconds as a unit of the TIME dimension, we are really talking about a time interval not a point in time. If we want to store and use a Date Time then we must define it as a datum measurements with the Christian, Muslim, Chinese and computer manufactures versions all using different points in real time as their zero datum. N.B. Only cosmologists can even begin to talk about absolute time or the non-existence of time and although they may be quite certain about what happened in the first few seconds I don't think they can say with the same precision how long ago that was. So an absolute datum measurement in the time dimension isn't really viable. **Height (as in altitude)** - It is sometimes convenient to record heights against a reference scale and enter them into a formula which will internally calculate the height differences that it works with. You may wish to record variously height above a particular point on the ground, height above sea level or height above the centre of the Earth. We can use datum measurement types for each of them which define how they are related and ensure that conversions are carried out correctly but height above the centre of the Earth has a case for being an absolute datum measurement. Zero above the centre of the Earth really does represent absolute non-existence of height and it is the measurement that can be applied directly as a factor in geometrical formulae in the most general sense.

The library (that is the engine for unit definitions) is made available for use simply by including a single header file (provided in the download) with no dependancies.

#include "**ulib.h**"

Most of the library is wrapped in a private namespace that you need never know about. Your access to it is unseen and carried out by the macros used to define units. You may wish to wrap the units that you define in a namespace but if you do so, make sure that **ulib.h** is included within that namespace.

namespace myunits{
#include "**ulib.h**"
#include "myunits.h" //This is where you define your units
}

**Macros used to define units** (quantities) are:

ulib_Dimension(1, LENGTH)
ulib_base_Unit(LENGTH, metres)
ulib_compound_Unit( metres_psec, =, metres, Div, secs)
ulib_Unit_as_inverse_of( Herz, secs )
ulib_scaled_Unit(Kms, =, 1000, metres)
ulib_precalc_Unit_to_Unit(feet_pmin, Kms_psec)

and **with multiple base unit systems**:

ulib_multiple_Bases(2)
ulib_all_bases_Unit(TIME, secs)
ulib_base2_Unit(LENGTH, cms, =, 0.01, metres)

**Type modifiers for unit declarations** are:

**sq_**metres
**cubic_**metres
**to_power**<secs, 4>**::type**
**inverse_of**<secs>**::type**

**Dot methods of unit type variables** are:

double variable.**as**<unit_type> ()
unit_type variable.**integer_root**<root_function, positive integer>()
bool variable.**is_approx**<tolerance_unit, numerator, denominator>(variable2)
int n=variable1.**goes_into**(variable2, Remainder)

**Named global functions** are:

double **as**<unit_type> (unit_variable)
root_unit_type **integer_root**<root_function, positive integer>(unit_variable)

**Macros used to define datum measurements** (reference points with respect to a datum) are:

ulib_datum_Measurement(**celcius**, degrees_c, 0)
ulib_absolute_datum_Measurement(**kelvin**, degrees_c, 273)

**Promotion of absolute datum measurement variable to normal quantity unit** by postfixing with **()**

kelvin **abs_temp**=200;
Kgs mass=5;
Kelvin_Kgs KKgs =**abs_temp()** * mass;
**abs_temp()** = KKgs / mass;
KKgs = **kelvin(200)()** * mass;

**Global functions** **depreciated from the library are:**

root_unit_type **Sqrt**(unit_variable)
bool **approx_equal**<tolerance_unit, numerator, denominator>(numeric_variable1, numeric_variable2)
int **how_many_in**(U const& D, U2 const& N, U3 & R)

These can be activated with the **ulib_using_depreciated**(function_name)

macro

## Encapsulation of dimensional analysis

Barton’s and Nackman’s approach to dimensional analysis (mentioned earlier) is encapsulated by `struct dimensions`

which has no data members because it is only involved in type formation and type matching at compile time.:

template <int d1, int d2, int d3, int d4, int d5, int d6, int d7>
struct **dimensions**
{
enum { D1=d1, D2=d2, D3=d3, D4=d4, D5=d5, D6=d6, D7=d7 };
};

`enum`

is used as a convenient way to declare a list of `static const int`

members and initialise them. In this case they record the integer template parameters that define the class. As `enum`

s they are not allocated memory as variables and are simply inserted into runtime code wherever they appear, which in this case, is nowhere. As a result they act as pure compile time constants.

To work with this, structs are provided holding typedefs of `dimensions`

structs representing operations on and combinations of other `dimensions`

structs:

- An explicit single dimension:

template <int num> struct **single_dimension**
{
typedef dimensions<(1==num)?1:0, (2==num)?1:0, (3==num)?1:0,
(4==num)?1:0, (5==num)?1:0, (6==num)?1:0, (7==num)?1:0>
**dims**;
};
single_dimension<1>::dims LENGTH;

- A power of an existing
`dimensions`

struct

template<class dims_in, int P> struct **dims_to_power**
{
typedef dimensions<dims_in::D1*P, dims_in::D2*P, dims_in::D3*P,
dims_in::D4*P, dims_in::D5*P, dims_in::D6*P, dims_in::D7*P>
**dims**;
};

- Multiplication (op=
`Mult`

) and Division (op=`Div)`

of one unit by another:

template<class t, class **op**, class b> struct **Combine_dims**;
struct **Div**{}; struct **Mult**{};
template<class dims1, class dims2> struct Combine_dims<dims1, **Mult**, dims2>
{
typedef dimensions<
dims1::D1 + dims2::D1, dims1::D2 + dims2::D2,
dims1::D3 + dims2::D3, dims1::D4 + dims2::D4,
dims1::D5 + dims2::D5, dims1::D6 + dims2::D6,
dims1::D7 + dims2::D7>
**dims**;
};
template<class dims1, class dims2> struct Combine_dims<dims1, **Div**, dims2>
{
typedef dimensions<
dims1::D1 - dims2::D1, dims1::D2 - dims2::D2,
dims1::D3 - dims2::D3, dims1::D4 - dims2::D4,
dims1::D5 - dims2::D5, dims1::D6 - dims2::D6,
dims1::D7 - dims2::D7>
**dims**;
};

- Unit type of integer root of a unit type.
*A result that cannot be represented by integer powers of the base dimensions will produce a compile time error:*

template<class dims_in, int R> struct **integer_root_of_dims**
{
private:
template<int D, int D2, int N> struct div_if_first_param_zero
{ private: enum{ res= "error_unit_is_not_raised_to_this_power"} ; };
template<int D, int N> struct div_if_first_param_zero<0, D, N>
{ enum{ res= D/N}; };
template<int D, int N> struct div_exact :
public div_if_first_param_zero<(D/N)*N - D, D, N> {};
public:
typedef dimensions< div_exact<dims_in::D1, R>::res,
div_exact<dims_in::D2, R>::res,
div_exact<dims_in::D3, R>::res,
div_exact<dims_in::D4, R>::res,
div_exact<dims_in::D5, R>::res,
div_exact<dims_in::D6, R>::res,
div_exact<dims_in::D7, R>::res >
**dims**;
};

These structs, that do nothing other than hold a typedef, provide the tools of dimensional analysis. This allows `dimensions`

structs to be formed, operated on, combined and compared. Their existence and all the operations between them are exclusively compile time,

## Architecture of a unit type

The basic architecture of a unit type is illustrated in a private macro called by the base unit definition macros:

#define ULIB_BASE_UNIT(sys, dimension, name) \
\
struct ULIB_DESC_CLASS(name) : public **dimension** \
{ \
enum { **Scaled**=ung::_UNSCALED, **System**=sys }; \
template <int P> inline static double **Base2This**(double val) *{return val;}* \
template <int P> inline static double **This2Base**(double val) *{return val;} * \
}; \
\
typedef ung::named_unit<ULIB_DESC_CLASS(name),1> **name**; \
typedef ung::named_unit<ULIB_DESC_CLASS(name),2> **sq_**##**name**; \
typedef ung::named_unit<ULIB_DESC_CLASS(name),3> **cubic_**##**name**;

The `ULIB_DESC_CLASS(name) `

macro simply adds an underscore to the beginning of the name you have passed in to be used as the name for a hidden description struct. The description struct inherits the `dimensions`

struct you have passed in, sets enums to indicate if it is scaled and which base unit system it uses and provides two functions whose generic purpose is to convert any unit to its base unit and back. In this case we are defining a base unit so these functions simply return the value passed in - that is they have no effect. Even without the inline modifier, compilers are good at recognising such a simple case of no effect and the function will not even be called.

The unit name you passed in becomes a typedef of a `<code>named_unit`

`<>`

passed the description struct as a template parameter. In the case of `metres`

this would expand as:

#define ULIB_BASE_UNIT(0, LENGTH, **metres**) \
\
struct *_metres* : public LENGTH \
{ \
enum { Scaled=ung::_UNSCALED, System=0 }; \
template <int P> inline static double Base2This(double val) {return val;} \
template <int P> inline static double This2Base(double val) {return val;} \
}; \
\
typedef ung::named_unit<*_metres*,1> **metres**; \
typedef ung::named_unit<*_metres*,2> **sq_metres**; \
typedef ung::named_unit<*_metres*,3> **cubic_metres**;

When you declare a unit type (`metres`

) you are really declaring a `named_unit<>`

class which is passed the units description class (`_metres`

) as a template parameter. It is the `named_unit<> `

template class that encapsulates the common behaviour of the unit types that you declare.

template <
class **T**, int **P**=1,
class *dims*=typename dims_to_power<T, P>::dims,
int *Sys*=T::System
>
class **named_unit**
{

The

`named_unit<>`

template class takes the unit description class

`T`

and the power

`P`

as template parameters and from them creates defaults for its template parameters

`dims`

(using the

`dims_to_power`

type modifier

described above) and

`Sys`

. It is this class that determines whether an operation between units can be done and the dimensions of the resulting unit type. But the task of carrying out numerical calculations is largely delegated to specialisations of the structs:

`Unit2Unit<>`

and

`Base2Base<>`

which carry out any necessary conversions.

For example the following method allows construction of one named_unit from another

template <class T1, int P1, int Sys2> inline **named_unit::named_unit**(**named_unit**<T1,P1, dims, Sys2> const& S)
{val=Unit2Unit<T1, T, P1, P>::Convert(S.Value());}

This will accept a named unit with any description class `T1`

, raised to any power `P1`

and from any base unit system `Sys2`

but the dimensions template parameter must match its own dimensions template parameter `dims`

. Having ensured that this operation can only be allowed if the units have matching dimensions, the actual assignment is carried out by `Unit2Unit<T1, T, P1, P>::Convert(S.Value())`

. In many cases, for instance assignment by a unit of the same type, there is nothing for `Unit2Unit<T1, T, P1, P>::Convert(S.Value())`

to do and nothing will be done - that is no code will be generated to do anything. This will happen when the general form of `Unit2Unit`

...

template <class T1, class T2, int P1=1, int P2=1>
struct **Unit2Unit**
{
inline static double Convert(double val)
{
return T2::**Base2This**<P2>
(
**Base2Base**ByUnit<typename dims_to_power<T2, P2>::dims, T1, T2>
::Conv
(
T1::**This2Base**<P1>(val)
)
);
}
};

...encounters specialisations of `This2Base, Base2Base`

and `Base2This`

that do nothing. For instance operations bewteen base units, including unscaled compound units, of the same base unit system will find that the units' `Base2This`

and `This2Base`

methods will do nothing and that there also exists a specialisation of `Base2Base`

for cases where both bases are the same, which also does nothing. The result is that it will be clear to the compiler that `Convert`

has no effect so no code, not even a call, will be generated. The work of going through all this in order to decide that nothing needs to be compiled is of course done by the compiler - and it won't choke on it, there are no deep recursions.

In cases where conversions are indeed necessary the general form of `Unit2Unit<>`

will only compile code for those parts of the conversion that are necessary. However in the case of conversion between compound units of different base unit systems, this can involve a string of multiplications.

The general form of `Base2Base<>`

is:

template <class dims, int Sys1, int Sys2> struct **Base2Base**
{
inline static double Conv(double val)
{return
BaseShifter<dims::D1, 1, Sys1, Sys2>::Get(
BaseShifter<dims::D2, 2, Sys1, Sys2>::Get(
BaseShifter<dims::D3, 3, Sys1, Sys2>::Get(
BaseShifter<dims::D4, 4, Sys1, Sys2>::Get(
BaseShifter<dims::D5, 5, Sys1, Sys2>::Get(
BaseShifter<dims::D6, 6, Sys1, Sys2>::Get(
BaseShifter<dims::D7, 7, Sys1, Sys2>::Get(val)))))));}
}

where the various specialisations of `BaseShifter<>`

(one for each potential dimension) are provided as units are declared. A default specialisation is provided for all cases where the dimensions power `dims::D1`

etc. is zero:

template <int Dimension, int Sys1, int Sys2>
struct **BaseShifter**<**0**, Dimension, Sys1, Sys2>
{
inline static double Get(double v){return v;}
};

This ensures that the dimensions you haven't even named and dimensions not involved in the conversion automatically produce base shifters that have no effect.

Despite these efficiencies a conversion between scaled units of force from different base unit systems could involve four multiplications. For any two units with the same dimensions this may be reduced to one multiplication by using the macro `ulib_precalc_Unit_to_Unit(unit1 , unit2)`

#define **ulib_precalc_Unit_to_Unit**(unit1, unit2) \
\
**const double** ULIB_UNIT2UNIT_RATIO(unit1, unit2)= \
ULIB_DESC_CLASS(unit2)::Base2This<1>( \
ung::Base2Base \
<typename ung::dims_to_power<ULIB_DESC_CLASS(unit1), 1>::dims, \
ULIB_DESC_CLASS(unit1)::System, ULIB_DESC_CLASS(unit2)::System> \
::Conv( \
ULIB_DESC_CLASS(unit1)::This2Base<1>(1) \
) \
); \
\
template <int P> struct ung::**Unit2Unit** \
<ULIB_DESC_CLASS(unit1), ULIB_DESC_CLASS(unit2), P, P> \
{ \
typedef ung::equal_dimensions \
<ULIB_DESC_CLASS(unit1), ULIB_DESC_CLASS(unit2)> check_equal_dimensions; \
\
ULIB_DECLARE_POWX(ULIB_UNIT2UNIT_RATIO(name, unit2)) \
inline static double Convert(double val) {return powX<P>()*val;} \
}; \
template <int P> struct ung::**Unit2Unit** \
<ULIB_DESC_CLASS(unit1), ULIB_DESC_CLASS(name), P, P> \
{ \
ULIB_DECLARE_POWX(ULIB_UNIT2UNIT_RATIO(unit1, unit2It)) \
inline static double Convert(double val) {return (double)1/powX<P>()*val;} \
};

It starts by declaring a global `const double `

that will be initialised at load time (not compile time) by making the same calls as `Unit2Unit<>`

would. It cannot call `Unit2Unit<>`

to do this because that would instantiate the general form for this combination and defeat the purpose. It then defines a specialisation of `Unit2Unit<>`

for each direction between the two units that makes direct use of the precalculated global `const double`

. Any conversions between these two units will then always find these specialisations and perform the conversion with a single operation.

Most operations on a `named_unit<>`

will return a `named_unit<>`

of the same type but multiplication or division by other units will return a unit type with different dimensions that may not correspond with any unit you have defined. This is particularly likely to happen with intermediate values in complex calculations. For this reason there needs to be a mechanism for dealing with unnamed unit types. This is provided by the template class `x_unit<>`

.

template <class dims, int Sys> class **x_unit**
{

An `x_unit<>`

does not have a name or unit description class. Its identity is determined only by its dimensions (`dims`

) and its base unit system (`Sys`

). Its units are always those of its own base unit system.

`named_unit<>`

and `x_unit<>`

are the only data structures involved in that they have a data member of type `double`

holding the quantity they represent in their own units. They are the replacements for the raw doubles that would otherwise have been involved. Both have to provide all of the operations that can occur with those raw doubles. There are some differences in this area between `named_unit<>`

and `x_unit<>`

: `named_unit<>`

s are declared in your code. They are variables you can do things with so they need to support `+=`

, `-=`

and assignment `=`

. Conversely `x_unit<>s`

are not just unnamed types they are also temporary variables that have no name with which you can work. You do not declare them, they are created automatically and invisibly as intermediate steps in evaluating expressions that you have written. They do not need to support `+=`

, `-=`

and assignment `=`

because as unnamed temporaries it is impossible for those operations to be called.

Now we can look at what happens when you divide one unit type variable by another, lets say metres by seconds.

metres dist=10;
secs time=5
metres_per_sec velocity = **dist / time;**

`dist`

is of type `metres`

which is a typedef of `named_unit<_metres, 1>`

and `time`

is of type `secs`

, typedef of `named_unit<_secs, 1>`

so the operator `/`

of `named_unit<>`

that takes another `named_unit<>`

will be called:

template <class T1, int P1, class dims2> inline x_unit<
typename **Combine_dims**<dims, **Div**, dims2>**::dims**,
**SysFilter**<Sys,T1::System>**::sys**
>
**named_unit**::**operator /** ( **named_unit**<T1, P1, dims2> b) const
{
return **UnitByUnit**<T,T1,P,P1>::Divide(Value(), b.Value());
}

This method will take a `named_unit<>`

of any name, to any power and from any base unit system. This is represented by the introduction of new template arguments...

template <**class T1, int P1, class dims2**>

...and their unqualified application to completely fulfill the type of the value argument

**operator /** ( named_unit<**T1, P1, dims2**> b) const

typename **Combine_dims**<dims, **Div**, dims2>**::dims**

**SysFilter**<Sys,T1::System>**::sys**

The actual numerical division is carried out by struct `UnitByUnit<>`

template <class T1, class T2, int P1=1, int P2=1>
struct **UnitByUnit**
{
private:
inline static double **OtherToThisBase**(double val)
{
return Base2BaseByUnit<typename dims_to_power<T2, P2>::dims, T1, T2>
::Conv
(
T2::This2Base<P2>(val)
);
}
public:
inline static double **Divide**(double v1, double v2)
{return T1::This2Base<P1>(v1) / OtherToThisBase(v2);}
inline static double Multiply(double v1, double v2)
{return T1::This2Base<P1>(v1) * OtherToThisBase(v2);}
};

This ensures that all conversions are carried out to put both operands in base units of the left operand's base unit system. In this case, both operands are in base units of the same base unit system so due to collapsing of inline functions that have no effect, the call to `OtherToThisBase`

will disappear in the compilation and only `v1/v2`

will be compiled.

The return value of the operator `/`

overload will be an `x_unit<>`

**x_unit**<
typename **Combine_dims**<dims, Div, dims2>::dims,
**SysFilter**<Sys,T1::System>::sys
>

So what happens next with this `x_unit<>`

? It may be invisibly combined with other `x_unit<>s`

or `named_unit<>s`

to form further `x_unit<>s`

but eventually you will have to capture the result as a `named_unit<>`

to be able to do anything with it and this is where you have to get your units right as we do when we write:

**metres_per_sec** velocity = **dist / time**;

`velocity`

is a `named_unit<>`

which has been passed `_metres_per_sec`

as a description class and it is being constructed from the result of `dist / time`

which will be an `x_unit<>`

.

`named_unit<>`

has a constructor taking an `x_unit<>`

as an argument:

template<int Sys2> inline **named_unit::named_unit**(**x_unit**<dims, Sys2> const& S)
{
val=ConvertFromXUnit<Sys2>(S.Value());
}

This constructor will take an `x_unit<>`

of any base unit system (new template parameter `Sys2`

) but it must have the same `dimensions`

struct as the `named_unit<>`

(template parameter `dims`

of the `named_unit<>`

). If we had written...

Newtons force = dist / time;

...then we would get a compile error telling us that this is wrong, the `x_unit<>`

produced by `dist / time`

does not have the same dimensions as `Newtons`

.

The value of the `named_unit`

is set by a call to the private member `ConvertFromXUnit`

...

template<int Sys2> inline static double **ConvertFromXUnit**(double val)
{
return T::Base2This<P>(
Base2BaseBySys<dims, Sys2, Sys>::Conv(val)
);
}

...which does any conversions necessary. In this case the `metres_per_sec`

unit is already in base units so `ConvertFromXUnit`

will do nothing and the constructor will compile to:

template<int Sys2> inline named_unit::named_unit(x_unit<dims, Sys2> const& S)
{
*val=S.Value();*
}

If you are working with a single base unit system then all System template parameters such as `Sys2 `

above will always be zero and they will play no real part in the action. However if you are working with multiple base unit systems then it is the parameter that distinguishes between them. At first sight it may seem enough that each base unit system is represented by a distinct number but what number do you use to represent units such as seconds which are typically shared by more than one base unit system (e.g. MKS and CGS)? The solution I chose works as follows:

Each base unit system has an internal number used for the `int System`

template parameter. Each is an integer power of 2 so that each base unit system is represented by a binary bit. We indicate that a unit is agnostic across base unit systems by intialising its `System`

enum with a logical OR of the base unit systems to which it belongs.

We need a way of determining which base unit system should be used for the temporaries (`x_units<>`

) in which calculation continues and we need to recognise that the ORed values (representing units such as seconds) are accepted by all the base unit systems as being of thier own. The is done by template struct `SysFilter<>`

:

template<int **Sys1**, int **Sys2**> struct **SysFilter**
{
private:
template<int SysANDSys2, int Sys> struct SysFilterReturn { enum {sys=SysANDSys2}; };
template<int Sys> struct SysFilterReturn<0, Sys> { enum {sys=Sys}; };
public:
enum {sys = SysFilterReturn<Sys1 & Sys2, Sys1>**::sys**};
};

`SysFilter`

is passed two `int Sys`

template parameters. If the logical AND of them is non zero then they are either the same or one of them is a combined bases symbol (2 or more bits set, as used for example by seconds), that matches the other base system. In this case there will be no change of base system the logical AND ensures that only one bit is propagated. However if the logical AND evaluates as zero then there is a change of base unit system and the first `int Sys`

template parameter is used unmodified as the new working base. In the case that both `Sys`

template parameters are combined bases symbols such as seconds*seconds, there will be a propagation of the 2 bits of the combined bases symbol but it doesn't matter, one of those bits will be removed as soon as it encounters any unit that is not base system agnostic. `SysFilter<>`

initialises its `sys`

enum using struct `SysFilterReturn<>`

in order to pass it the `Sys1 & Sys2`

, `Sys1`

construction. `SysFilterReturn<>`

, in turn uses template specialisation to provide an appropriately initialised sys enum. Its usage is `SysFilter<Sys1, Sys2>::sys`

.

`SysFilter<>`

is used wherever two base unit system numerical codes (`System`

enum) need to be compared or combined. Much of its use is encapsulated in the `Base2BaseBySys<>`

& `Base2BaseByUnit<>`

template structs which are used as convenience wrappers for base to base conversions.

template <class dims, int Sys1, int Sys2> struct **Base2BaseBySys**
: public *Base2Base
<dims, ***SysFilter**<Sys1,Sys2>::sys,
**SysFilter**<Sys2,Sys1>::sys>
{};
template <class dims, class T1, class T2> struct **Base2BaseByUnit**
: public *Base2Base
<dims, ***SysFilter**<T1::System,T2::System>::sys,
**SysFilter**<T2::System,T1::System>::sys>
{};

Code changes in the second release

Greatly assisted by the attention and diligence of others I have made some code changes for the second release.

**1.** The application of some fairly straightforward partial specialisation to deal generically with self to self conversions rather than generate them one by one with macros. These include

Self to self for Unit2Unit -* ***which fixed a run-time inefficiency when adding units of the same type **

template <typename T, int P>
struct Unit2Unit<T, T, P, P>
{
inline static double Convert(double val)
{
return val;
}
};

Self to self for Base2Base - *which fixed an unintended requirement to include ***ulib_multiple_Bases(1) when only using one base system**.

template <class dims, int Sys> struct ung::Base2Base<dims, Sys, Sys>
{
inline static double Conv(double v)
{
return v;
}
};

and any base to any base for the specific dimension of an all bases unit - *which replaced a lot of macro generation.*

#define ulib_all_bases_Unit(dimension, unit) \
\
ULIB_BASE_UNIT(ulib_OredBases, dimension, unit) \
template <int P, int Sys1, int Sys2 > \
struct ung::BaseShifter<P, dimension::Num, Sys1, Sys2> \
{ inline static double Get(double v){return v;} }; \
template <int Sys1, int Sys2> \
struct ung::BaseShifter<0, dimension::Num, Sys1, Sys2> \
{ inline static double Get(double v){return v;} };

**2. **The provision of a new unit definition macro allowing a unit to be defined as the inverse of another

**ulib_Unit_as_inverse_of**(name, orig)

This is an adaptation of `ulib_compound_Unit(name, is, t, Operation, b) `

with the first term replaced by a scalar (zero dimensions).

**3.** Review and rationalisation of dot methods and free functions.

The `as<>()`

function, designed to force you to specify units as a template when extracting the numerical value from a unit type...

template<class U>
inline double const& **as**(U const& nu)
{return ung::_as<U>(nu);}

...suffers from two faults:

- You don't have to specify a template parameter because the compiler can deduce it.
- It will accept almost anything as an argument and with such a short name it could clash with other libraries

The following replacement solves both problems. The compiler will only consider arguments that are x_units and named_units (there is also one for datum_units) and a correct unit type template must be supplied.

template <class U, class dims, int Sys>
inline double const& **as**(ung::x_unit<dims, Sys> const& nu)
{return nu.as<U>();}
template<class U, class T, int P>
inline double const& **as**(ung::named_unit<T, P> const& nu)
{return nu.as<U>();}

`as`

, `integer_root`

,` is_approx`

and `goes_into`

are now available as dot methods of named_units and x_units (datum_units only support the as and is_approx method).

The` Sqrt`

, `approx_equal `

and `how_many_in global`

functions have been depreciated for reasons of library integrity.

**4.** Addition of datum measurements data type.

This is represented in code by the `datum_unit`

class template. It is similar to the `named_unit`

class template but supports more limited functionality.

template <
class D, class T, class dims=typename dims_to_power<T, 1>::dims,
int Sys=T::System
>
class **datum_unit**

Datum measurement types are defined using the `ulib_datum_Measurement(Celcius, degrees_C, 0)`

macro:

#define **ulib_datum_Measurement**(name, orig, offset) \
struct ULIB_DESC_CLASS(name) \
{ \
enum {absolute=0}; \
static double DoOffset(double val){return val+(offset);} \
static double UndoOffset(double val){return val-(offset);} \
}; \ \
typedef ung::**datum_unit**<ULIB_DESC_CLASS(name), ULIB_DESC_CLASS(orig)> name;

...which defines a description cass for the datum measurement providing offset information and passes that and the description class of the `named_unit`

on which it is based, to a `datum_unit`

.

The `datum_unit`

class template uses the datum measurement description class to calculate offsets and the `named_unit`

description class to calculate dimensional suitability.

Absolute datum measurement types are defined using the `ulib_absolute_datum_Measurement(Celcius, degrees_C, 0)`

macro:

#define ulib_absolute_datum_Measurement(name, orig, offset) \
struct ULIB_DESC_CLASS(name) : public ULIB_DESC_CLASS(orig) \
{ \
enum {absolute=1}; \
static double DoOffset(double val){return val+(offset);} \
static double UndoOffset(double val){return val-(offset);} \
}; \
typedef ung::datum_unit<ULIB_DESC_CLASS(name), ULIB_DESC_CLASS(orig)> name;

This has two differences:

The datum measurement description class inherits from the `named_unit`

description class. This gives it a set of dimensions that allows the absolute datum measurement to be used to form compound units.

The `absolute`

enum is set to 1. This enables an `operator()()`

in the `datum_unit`

class

typename if_true_use_type_else_unknown
<
D::absolute,
named_unit<T, 1, dims, Sys>
> ::result_type & operator()()
{
return reinterpret_cast<named_unit<T, 1, dims, Sys>& >(*this);
}

that returns a reference cast as a `named_unit.`

The `if_true_use_type_else_unknown`

class template defines `result_type`

as a `named_unit<T, 1, dims, Sys>`

if `D::absolute`

is true or as an unknown class if if `D::absolute`

is false.

It might look like there is a lot of code wrapped around each unit type but it is all compile time book keeping. All that gets compiled is doubles and the combinations of them that you write in your code - just as if all units had been declard as doubles. The only extra code generated is the automatic scaling between units, which you would otherwise have had to write explicitly.

To clarify the compile time nature of this library: Only data requires run-time storage and only functions and methods that do something will generate run-time code. Structs and classes that only consist of enums and typedefs and functions and methods that are called but have no effect have no runtime presence whatsoever. If you use scaled units then memory will be allocated at runtime to store all of the conversion factors involved and there will be an execution of their initialisation at load time. The application of those scale factors is of course code that would have to exist anyway.

To fully understand how everything works together you will have to look at the source code. It is quite readable and well commented.

## History

First release - September 2014

Second release - December 2014

- Fix- Unintended requirement to include
` `

**ulib_multiple_Bases(1)**

when using only one base unit system. - Fix- Unintended run time inefficiency when adding units of the same type.
- Fix- No easy way of defining a unit as an inverse of another. A new
**ulib_Unit_as_inverse_of( Herz, secs) ** unit definition macro is now available for this. - Fix- Was possible to use the
**as<>()**

free function without supplying a unit type as a template parameter. - Fix-
**as<>()**

is now also available as a dot method of unit type variables. - Fix
** **- Global functions that bring dependancies or that could clash with other libraries depreciated. - Enhancement - Addition of
**datum measurements** for measurements such as temperature in Celsius and Farenheight whose numerical value does not represent a quantity or amount but rather a point on a reference scale with an arbritrary datum as zero.

Comments on code changes in the second release are collected here.

Software Author with engineering, science and mathematical background.

Many years using C++ to develop responsive visualisations of fine grained dynamic information largely in the fields of public transport and supply logistics. Currently interested in what can be done to make the use of C++ cleaner, safer, and more comfortable.