UNITS OF MEASUREMENT
FOR ADA

version 2.0
by Dmitry A. Kazakov

(mailbox@dmitry-kazakov.de)
[Home]

This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As a special exception, if other files instantiate generics from this unit, or you link this unit with other files to produce an executable, this unit does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Public License.


[TOC][Next]

Foreword

The need of support for handling measurement units does not require any approval. It is in particular true for the Ada programming language with its stress on safety and maintainability. The proposed set of packages represents a possible solution of the unit problem. Dimension checking is performed at run-time, so performance is sacrificed for safety. However, an attempt was made to reduce performance hit. The type Measure used to represent dimensioned values can be constrained to a specific unit. Operations on constrained subtypes of Measure could be potentially as efficient as floating-point operations. They are declared inlined, so that at least theoretically, a decent compiler could perform discriminant calculation and check at compile time.

The key features of the approach:

Download units_2_0.tgz (tar + gzip, Windows users may use WinZip) [Download]

You may wish to visit this site devoted to the problem of dimensioned values in Ada.

The units converter is a small dialog box where you can enter a dimensioned value and then convert it to SI:

screen shot

Ready-to-use executables:

Windows x86  [Download]
Linux_GTK i686 statically linked  [Download]

The source code is discussed in the section 3.2.


[Back][TOC][Next]

1. Types

Two types are used for dealing with units. The type Unit denotes the dimension of a physical entity. The type Measure represents a dimensioned value it is defined in a generic package Measures which is instantiated with the desired floating-point type as the parameter:

generic
   type Number is digits <>;
package Measures is
   ...

The package Float_Measures is an instance of Measures for the standard Float type: .

[Back][TOC][Next]

1.1. The type Unit

The type Unit is defined in the package Units. A value of the type Unit corresponds to a kind of physical entity, like energy, charge or velocity. The package itself is rather useless, because there are few things one can do with units. They are:

Or in terms of Ada the following functions are defined:

function "**" (Left : Unit; Right : Integer) return Unit;
function "*"  (Left, Right : Unit) return Unit;
function "/"  (Left, Right : Unit) return Unit;
function Sqrt (X : Unit) return Unit;

Values of the type Unit build a group. A value of Unit type can be viewed as an unordered set of seven base components (mass, length, time, current etc.). Each component has integer exponent part. The function Sqrt is defined for only units which components have even exponent parts. The exception Constraint_Error is propagated when the result of an operation is illegal.

The package also declares the exception Unit_Error and the type:

type Code_Set is (ASCII_Set, Latin1_Set, UTF8_Set);

1.1.1. Base units

The child package Units.Base defines the base units:

Current       : constant Unit;
Luminescence  : constant Unit;
Temperature   : constant Unit;
Mass          : constant Unit;
Length        : constant Unit;
Quantity      : constant Unit;
Time          : constant Unit;
Unitless      : constant Unit;

1.1.2. Unit constants

The child package Units.Constants defines some of more complex units. Geometric units are:

Area          : constant Unit := Length ** 2;
Volume        : constant Unit := Length ** 3;

Units used in mechanics:

Velocity      : constant Unit := Length / Time;
Acceleration  : constant Unit := Length / Time ** 2;
Force         : constant Unit := Mass * Acceleration;
Pressure      : constant Unit := Force / Area;
Energy        : constant Unit := Force * Length;
Power         : constant Unit := Energy / Time;

Electricity units:

Charge        : constant Unit := Current * Time;
Potential     : constant Unit := Energy / Charge;
Capacitance   : constant Unit := Charge / Potential;
Resistance    : constant Unit := Potential / Current;
Conductance   : constant Unit := Current / Potential;
Inductance    : constant Unit := Potential * Time / Current;

Chemistry units:

Concentration : constant Unit := Quantity / Volume;
Density       : constant Unit := Mass / Volume;

Optic units:

Luminance     : constant Unit := Luminescence / Area;

Other units:

Frequency     : constant Unit := Unitless / Time;

1.1.3. Conversion to ASCII and Latin-1 strings

The package Units.Edit provides the function Image is used to convert Unit to String:

function Image
         (  Value  : Unit;
            Latin1 : Boolean := True
         )  return String;

The syntax of the result string is:

<result>  ::=  <list>/<list> | 1/<list> | <list> | 1
<list> ::= <item> [<*><list>]
<item> ::= <base-unit>[<power>]
<base-unit> ::= A | cd | K | kg | m | mol | s

Here <*> is either * (if Latin1 is false), or · from Latin-1 character set. When Latin1 is false <power> is always ^<number>. With Latin-1 character set enabled, powers 2 and 3 are indicated using the corresponding superscript characters: 2 and 3. The following table lists the Latin-1 characters used for output:

Special characters
Character Latin-1 Meaning
2 B216 Power 2
3 B316 Power 3
· B716 Multiplication operator

1.1.4. Conversion to UTF-8 encoded strings

The package Units.UTF8_Edit provides the function Image used to convert Unit to an UTF-8 encoded string:

function Image (Value : Unit) return String;

The syntax of the result string is:

<result>  ::=  <list>/<list> | <list> | 1
<list> ::= <item> [·<list>]
<item> ::= <base-unit>[<power>]
<base-unit> ::= A | cd | K | kg | m | mol | s
<power> ::= [-]<number>
<number> ::= <digit>[<number>]
<digit> ::= 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Powers are output as superscript digits. When all powers are negative, the output uses no fraction but negative powers instead. For example: m-2 instead of 1/m2. The following table summarizes the list of special characters and their encodings in UTF-8:

Special characters
Character UTF-8 encoding Code point Meaning
2 C216 B216 00B216 Power 2
3 C216 B316 00B316 Power 3
4 E216 8116 B416 207416 Power 4
5 E216 8116 B516 207516 Power 5
6 E216 8116 B616 207616 Power 6
7 E216 8116 B716 207716 Power 7
8 E216 8116 B816 207816 Power 8
9 E216 8116 B916 207916 Power 9
- E216 8116 BB16 207B16 Power -
· C216 B716 00B716 Multiplication operator

1.1.5. Design notes

Some words about the design. I didn't make the type Unit private because then it could not be used as a discriminant of the type Measure (see the package Measures). To have Measure discriminated seems to be very important. It allows to define subtypes of Measure with fixed Unit component. For the same reason and for performance sake too, Unit is a modular number. As the result it inherits a number of operations which are disallowed by making them abstract. In the current implementation the type Interfaces.Unsigned_32 is used. This determines the range of the exponent part of a base component as -8..7. With Interfaces.Unsigned_64 the range would be -512..511. The following fragment of the file units.ads can be modified if the compiler supports modular numbers larger than Interfaces.Unsigned_32:

with Interfaces;

package Units is
   --
   -- Here we define the type used to represent units. It should be a
   -- modular type with a defined operation Shift_Right.
   --

   subtype UnitPower is Interfaces.Unsigned_n;
   . . .

Rational powers are not allowed, because the choice of base units in SI ensures whole powers of all physical values. Examples like Schottky-Langmuir equation can be easily written in a correct form with whole powers. Often it is argued that rational powers might be useful for dealing with intermediate results. A simple example x = ln ex shows that in general case it cannot be done anyway.

[Back][TOC][Next]

1.2. The type Measure

A value of the type Measure (defined in the package Measures) represents some quantity of a physical unit:

type Measure (SI : Unit := Units.Base.Unitless) is record
   Gain   : Number;
   Offset : Number'Base := 0.0;
end record;

Measures can be added, subtracted, multiplied, divided and exponentiated. The type Measure is a discriminated record type. The discriminant SI has the type Unit. Its value defines the dimension of a Measure. Unconstrained instances of Measure can be declared as:

Entity : Measure;      -- May hold value of any measurement unit
. . .
Entity := 5.0 * A / s; -- Set value 5 A/s
Entity := 3.0 * km;    -- Set value 3.0 km
Entity := Entity + s;  -- Illegal (km + s), Unit_Error

It is possible however, to create constrained subtypes of Measure capable to hold values of only a specific dimension. For instance:

subtype Speed is Measure (Velocity);
Car_Speed : Speed;          -- Only velocities are allowed
. . .
Car_Speed := 10.0 * km / h; -- OK
Car_Speed := A;             -- Illegal, Constraint_Error

The type Measure has two components: Gain and Offset. To get a SI equivalent of the value one should sum Gain and Offset. This is what the function Get_Value actually does. For instance:

Meter : Measure := (Length, Gain=>1.0,    Offset=>0.0);
Foot  : Measure := (Length, Gain=>0.3048, Offset=>0.0);

The subtype Dimensionless for dimensionless measures.

1.2.1. Shifted and unshifted Measures

The field Offset of the type Measure defines the value shift. Measures with zero Offset are called unshifted. They form a field with addition and multiplication operations:

Meter : Measure := (Length, Gain=>1.0,    Offset=>0.0);
Foot  : Measure := (Length, Gain=>0.3048, Offset=>0.0);
. . .
Meter * Foot -- 0.3048 square meters
Meter + Foot -- 1.3048 m
Meter ** 2   -- 1 square meter

Most of physical units and all SI units are unshifted. Shifted measurement units are rare. Degrees of Celsius and Fahrenheit is an example of shifted units. The degree of Celsius can be defined as:

Celsius : Measure := (Temperature, Gain=>1.0, Offset=>273.15);

Actually, the field Offset should also be a discriminant of the type Measure. Unfortunately it is illegal in Ada. Measures of same dimension and shift form a set closed relatively addition, subtraction and multiplication to a number (i.e. a group). Therefore:

5.0 * Celsius     -- Legal, 5ºC = 278.15 K
Celsius + Celsius -- Legal, 2ºC = 275.15 K
Celsius * Celsius -- Illegal, Unit_Error is propagated

In other words, shifted measures can be neither multiplied nor divided, except when a shifted measure is multiplied to or divided by a dimensionless unshifted value as in the first example above. Note that addition and subtraction are only legal when dimension and shift are same for both operands:

Kelvin  : Measure := (Temperature, Gain=>1.0, Offset=>0.0);
Celsius : Measure := (Temperature, Gain=>1.0, Offset=>273.15);
. . .
Celsius + Kelvin -- Illegal, Unit_Error is propagated
Celsius * Kelvin -- Illegal, Unit_Error is propagated

Mixing of differently shifted measures is ambiguous. In the given example Celsius and Kelvin could be added if one would be converted to another:

Convert (Celsius, Kelvin) + Kelvin  -- +275.15 K (unshifted) = 2ºC
Celsius + Convert (Kelvin, Celsius) -- -271.15ºC (shifted)   = 2 K

Note that the numeric equivalent of the result depends on whether Celsius is converted to Kelvin or inversely. Even if two zeros (0ºC and 0 K) are added the results will differ:

Convert (0.0 * Celsius, Kelvin) + 0.0 * Kelvin  -- +273.15 K (0ºC)
0.0 * Celsius + Convert (0.0 * Kelvin, Celsius) -- -273.15ºC (0 K)

1.2.2. Operations defined on Measures

Unary operations:

function "abs" (Right : Measure) return Measure;
function "+"   (Right : Measure) return Measure;
function "-"   (Right : Measure) return Measure;

Exponentiation:

function "**" (Left : Measure; Right : Integer) return Measure;

Multiplication:

function "*" (Left : Number'Base; Right : Measure) return Measure;
function "*" (Left : Measure; Right : Number'Base) return Measure;
function "*" (Left, Right : Measure)               return Measure;

Division:

function "/" (Left : Number'Base; Right : Measure) return Measure;
function "/" (Left : Measure; Right : Number'Base) return Measure;
function "/" (Left, Right : Measure)               return Measure;

Addition and subtraction:

function "+" (Left, Right : Measure) return Measure;
function "-" (Left, Right : Measure) return Measure;

Comparisons:

function ">"  (Left, Right : Measure) return Boolean;
function "<"  (Left, Right : Measure) return Boolean;
function "="  (Left, Right : Measure) return Boolean;
function ">=" (Left, Right : Measure) return Boolean;
function "<=" (Left, Right : Measure) return Boolean;

The equality operator can be applied to any pair of measures. The inequality operation is implicitly defined by the compiler.

Scale shift (value destructive):

function "and" (Left : Measure; Right : Number'Base) return Measure;

The result of scale shift is formed by adding the value of the parameter Right to the field Offset. Note that the numeric SI equivalent of the result will differ from one of the parameter Left. For instance:

Celsius : constant Measure := K and 273.15; -- 1ºC (is not equal to 1 K)

See also the functions Normalize and Shift which perform scale shifts retaining the value.

The exception Constraint_Error is raised by the operations when the corresponding numeric operation does. This behavior depends on whether Number’Machine_Overflows is true, as Ada Reference Manual states in 4.5.5 (22). Also Constraint_Error is propagated out of multiplicative operations "*", "/" and "**" when the dimension of the result cannot be represented as Unit because of a base unit power overflow.

1.2.3. Unit conversions

The function Get_Value returns a SI equivalent of the argument:

function Get_Value (Value : Measure) return Number;

The function Get_Value_As returns a numeric equivalent of the first argument in the measurement units defined by the second argument:

function Get_Value_As (Value, Scale : Measure) return Number;

For instance Get_Value_As (T, Celsius) returns the value of T in degrees of Celsius. The arguments Value and Scale should have same units. Otherwise the exception Unit_Error is propagated.

function Normalize (Value : Measure) return Measure;

The function Normalize returns an unshifted measure with the numeric equivalent same as one of the argument. For example:

Zero : Measure := Normalize (0.0 * Celsius); -- 273.15 K (unshifted)

Here 0ºC is converted to its unshifted equivalent 273.15 K.

function Shift (Value : Measure, Shift : Number'Base) return Measure;

The function Shift returns the argument shifted to Shift items. Unlike the operation and, the numeric equivalent of the result is same as one of the first argument. The value of the parameter Shift is subtracted from the field Gain and added to the field Offset to produce the result:

Zero : Measure := Shift (0.0 * Celsius, -273.15); -- 273.15 K

Here 0ºC is converted to its unshifted equivalent 273.15 K. Note that Normalize (X) is equivalent to Shift (X, X.Offset).

function Convert (Value, Scale : Measure) return Measure;

The function Convert is used for measurement unit conversions. The value of the first argument is converted to the measurement units of the second argument. The arguments Value and Scale should have same dimension. Otherwise the exception Unit_Error is propagated. Convert (X, Y) is an equivalent of Shift (X, Y.Offset - X.Offset). This is null operation if both arguments have same Offsets. Otherwise, the result has the shift of the second argument.

function To_Measure (Value : Number) return Measure;

This function returns a dimensionless unshifted measure corresponding to Value.

1.2.4. Constants

Few constants of the type Measure are defined in the package Measures. They correspond to the base SI measurement units: 

Base SI constants
Constant The base SI unit
A Ampere
cd Candela
K Kelvin
kg Kilogram
m Meter
mol Mole
s Second

and dimensionless SI units:

Dimensionless constants
Constant Meaning
Np Neper*
rad Radian, plane angle
sr Steradian, solid angle
* Neper is not a SI unit. It is put here to have a constant for 1 SI other than rad. Actually Np = rad = sr

The generic package Measures_Derived defines the constants corresponding to the derived SI measurement units:

generic
   with package
Measures is new Standard.Measures (<>);
package
Measures_Derived is ...

The package defines the following constants:

Constants corresponding to the derived SI units
Constant Unit Comment
C Coulomb  
F Farad  
Gy Gray  
Henry Henry The full name Henry is used instead of H to reserve h for hour, which being a non-SI unit would be still more important for many than Henry
Hz Hertz  
J Joule  
lm Lumen  
lx Lux  
N Newton  
Ohm Ohm The short name (Greek omega Ω) does not belong to the Latin-1 character set
Pa Pascal  
Siemens Siemens The SI name S would conflict with s (seconds) defined in the package Measures. Therefore the full name is used instead
Sv Sievert  
Tesla Tesla The short name t is reserved for metric ton
V Volt  
W Watt  
Wb Weber  

The package Float_Measures_Derived is an instance of Measures_Derived for the standard type Float.

Further constants are defined in the generic package Measures_Irregular:

generic
   with package
Derived_Measures is new Measures_Derived (<>);
package
Measures_Irregular is ...

The package provides constants corresponding to various irregular units: 

Irregular units constants
Constant Unit of measurement
acre Acre
ang Ångström
are Are
atm Athmosphere
B Bel
BTU British thermal unit
Bq Becquerel
bar Bar
barleycorn Barleycorn
barn Barn
barrel Barrel
Ci Curie
cal Calorie
candle Candle
carat Carat
Celsius Degree of Celsius
ch Chain
cubit Cubit
d Day
dB Decibel
degree Degree (plane angle)
dyn Dyn
eV Electronvolt
ell Ell
erg Erg
Fahrenheit Degree of Fahrenheit
fathom Fathom
finger Finger
ft Foot
fpm Feet per minute
fps Feet per second
fur Furlong
G Gauss
gal Gallon
grain Grain
gram Gram. The SI unit for mass is kilogram
h Hour
hand Hand
hectare Hectare
hp Horsepower
INM International nautical mile
inch Inch
kcal Kilocalorie
kgf Kilogram-force
knot Knot
L Liter
lb Pound
line Line
link Link
ly Light year
mi Mile
min Minute
min_of_arc Minute of arc
mpg Miles per gallon
mph Miles per hour
mps Miles per second
nail Nail
Oe Oersted
oz Ounce
pace Pace
pc Parsec
percent Percent
point Point
ppb Parts per billion
ppm Parts per million
ppt Parts per trillion
psi Pounds per square inch
pt Pint
qt Quart
R Roentgen
rd Rod
rood Rood
rpm Revolutions per minute
rps Revolutions per second
sec_of_arc Second of arc
span Span
t Metric ton
tablespoon Table spoon
teaspoon Tea spoon
torr Torr (mmHg)
township Township
u Unified atomic mass
ua Astronomical unit
wineglass Wine glass
yd Yard
year Year

The package Float_Measures_Irregular is an instance of Measures_Irregular for the type Float.

1.2.5. Elementary functions

The generic package Measures_Elementary_Functions

generic
   type
Number is digits <>;
   with package
The_Measures is new Measures (Number);
package
Measures_Elementary_Functions is
   ...

provides the following functions:

function Sqrt (X : Measure) return Measure;

The function Sqrt is defined for the measures which unit has components with even exponent part. The exception Unit_Error is propagated otherwise. For instance:

Area : Measure := 25.0 * m**2;
Side : Measure;
. . .
Side := Sqrt (Area); -- OK, the result is 5 m
Side := Sqrt (Side); -- Error, Unit_Error propagates

Exponent and log functions require a dimensionless argument. The result is also dimensionless. Note that integer exponentiation is defined for all unshifted measures.

function Exp (X : Dimensionless) return Dimensionless;
function Log (X : Dimensionless) return Dimensionless;
function Log (X : Dimensionless; Base : Number'Base)
   return Dimensionless;

function "**" (Left : Dimensionless; Right : Dimensionless)
   return Dimensionless;
function "**" (Left : Dimensionless; Right : Number'Base)
   return Dimensionless;
function "**" (Left : Number'Base; Right : Dimensionless)
   return Dimensionless;

The trigonometric functions:

function Sin (X : Dimensionless) return Dimensionless;
function Cos (X : Dimensionless) return Dimensionless;
function Tan (X : Dimensionless) return Dimensionless;
function Cot (X : Dimensionless) return Dimensionless;

The argument of trigonometric functions is measured in radians (dimensionless). No variants with the parameter Cycle as in Ada.Numerics.Generic_Elementary_Functions are necessary, because arguments in degrees can be naturally expressed using units:

Cos (180.0 * degree); -- degree is declared in Measures_Irregular
Cos (3.1415 * rad);

The inverse trigonometric functions return the result measured in radians:

function Arcsin (X : Dimensionless) return Dimensionless;
function Arccos (X : Dimensionless) return Dimensionless;
function Arctan (X : Dimensionless) return Dimensionless;
function Arccot (X : Dimensionless) return Dimensionless;
function Arctan (Y, X : Measure) return Dimensionless;
function Arccot (X, Y : Measure) return Dimensionless;

Arctan and Arccot are defined for any pair of compatible unshifted measures (Unit_Error is propagated otherwise). For instance:

subtype Height is Measure (Length);
subtype Width is Measure (Length);
X : Width;
Y : Height;
Angle : Dimensionless; -- Radians
. . .
X := 25.0 * m;
Y := 30.1 * ft; -- ft (foot) is declared in Measures_Irregular
Angle := Arctan (Y, X);

Hyperbolical functions are defined on dimensionless argument:

function Sinh (X : Dimensionless) return Dimensionless;
function Cosh (X : Dimensionless) return Dimensionless;
function Tanh (X : Dimensionless) return Dimensionless;
function Coth (X : Dimensionless) return Dimensionless;

function Arcsinh (X : Dimensionless) return Dimensionless;
function Arccosh (X : Dimensionless) return Dimensionless;
function Arctanh (X : Dimensionless) return Dimensionless;
function Arccoth (X : Dimensionless) return Dimensionless;

The package instance for the type Float is named Float_Measures_Elementary_Functions.

1.2.6. Conversion from String

There are two encoding-specific generic packages responsible conversions of Measure to and from strings and one universal generic package for handling any encoding. The package Measures_Edit and the package Measures_UTF8_Edit are encoding-specific. Both packages have same formal parameters:

generic
   with package
Irregular_Measures is new Measures_Irregular (<>);
   with package Float_Edit is
      new
Strings_Edit.Float_Edit
          (  Irregular_Measures.Derived_Measures.Measures.Number
          );
package Measures_[UTF8_]Edit is ...

The package Measures_Edit is used for dealing with strings encoded in either ASCII or Latin-1 character sets. The package Measures_UTF8_Edit is used for the strings encoded in UTF-8 (see Unicode Transformation Format). The routines provided by the packages are almost identical and differ only the sets of symbols supported. (The packages Float_Measures_Edit and Float_Measures_UTF8_Edit are instantiations of Measures_Edit and Measures_UTF8_Edit for the standard type Float.)

The following subroutines are used for measures input:

procedure Measures_Edit.Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Latin1  : Boolean := True
          );
procedure Measures_UTF8_Edit.Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure
          );

The procedures Get input a measure from the string Source. The process starts from the Source (Pointer) position. After successful completion Pointer is advanced to the position following the measure taken from the string. The parameter Value accepts the measure. The flag Latin1 of Measures_Edit.Get indicates Latin-1 character set support. With Latin1 = true the set of recognized symbols is extended as shown in the table below. The variant Measures_UTF8_Edit.Get is used for UTF-8 encoded strings. It supports an even larger set of symbols:

Special characters
Character Latim-1 UTF-8 Code point Meaning
0 - E216 8116 B016 207016 Power 0
1 B916 C216 B916 00B916 Power 1
2 B216 C216 B216 00B216 Power 2
3 B316 C216 B316 00B316 Power 3
4 - E216 8116 B416 207416 Power 4
5 - E216 8116 B516 207516 Power 5
6 - E216 8116 B616 207616 Power 6
7 - E216 8116 B716 207716 Power 7
8 - E216 8116 B816 207816 Power 8
9 - E216 8116 B916 207916 Power 9
+ - E216 8116 BA16 207A16 Power +
- - E216 8116 BB16 207B16 Power -
· B716 C216 B716 00B716 Multiplication operator
° B016 C216 B016 00B016 Degree, also in °C, °F etc
µ B516 C216 B516 00B516 Micro
CE16 BC16 03BC16
°C - E216 8416 8316 210316 Celsius (one letter sign)
°F - E216 8416 AA16 210916 Fahrenheit (one letter sign)
K - E216 8416 AA16 212A16 K (Kelvin, one letter sign)
Å - E216 8416 AB16 212B16 Ångström (one letter sign)
Å C516 C316 8516 00C516 Å in Ångström
å E516 C316 A516 00E516 Small Å in Ångström
ö F616 C316 B616 00F616 Small O-umlaut in Ångström
Ω - CE16 A916 03A916 Ohm
E216 8416 A616 212616

The measure syntax is:

<measure>  ::=  ( <measure> )
<measure> ::= <measure> [<dyadic-operation>] <measure>
<measure> ::= <prefix-operation><measure>
<measure> ::= <measure><postfix-operation>
<measure> ::= <number>
<measure> ::= <unit>
<dyadic-operation> ::= ** | ^ | * | · | / | + | - | and
<prefix-operation> ::= + | -
<postfix-operation>  ::= <superscript-integer>

Space and tabs can be used as delimiters. Two consequent lexemes shall be separated by at least one delimiter if the first one ends with a letter while the second starts with a letter. For instance: 

As   Illegal, there is no any delimiter between A and s
A s OK, A*s
A·s OK, A*s
5A OK, 5*A

The exponentiation operation (** or ^) has the highest priority. Then the multiplication (*, · or empty) follows. The division (/) has lower priority than the multiplication, therefore kg/m*s is an equivalent to kg/(m*s). The lowest priority has the shift (and). The right argument of the exponentiation operation should be dimensionless and have zero fraction, i.e. be convertible to an integer without a precision loss. <superscript-integer> is recognized only in UTF-8 variant and when Latin1 = true, but then only the powers 1, 2 and 3 are supported. <unit> is a name denoting a measurement unit, such as foot: 

<unit>  ::=  <short-regular-unit> | <full-regular-unit> | <irregular-unit>
<short-regular-unit> ::= [<short-SI-prefix>] <short-unit-name>
<full-regular-unit> ::= [<full-SI-prefix>] <full-unit-name>

The following table defines <short-unit-name> and <full-unit-name>. Beware, all names are case sensitive:

Regular unit names (used with SI prefixes)
Short name(s) Full name(s) Comments
A ampere  
  bar  
B bel  
barn barn  
Bq becquerel  
C coulomb  
°C   In Latin-1 and UTF-8 encoded strings a combination of ring (00B016) and the capital letter C can be used. In UTF-8 the degree Celsius (210316) is also supported
cd candela  
Ci curie  
erg erg  
F farad  
G gauss  
g gram, grams, gramme, grammes  
Gy gray  
H henry  
Hz hertz  
J joule  
K kelvin In UTF-8 encoded strings, Kelvin sign (212A16) is recognized as well
L, l liter, liters, litre, litres  
lm lumen  
lx lux  
m meter, meters, metre, metres  
mol mole  
N newton  
Ω ohm, Ohm In UTF-8 encoded strings both Unicode Greek omega (03A916) and the Ohm (212616) sign are recognized
Pa pascal  
R roentgen  
rad radian  
S siemens  
s second, seconds  
sr steradian  
Sv sievert  
T tesla  
t ton, tons, tonne, tonnes  
V volt  
W watt  
Wb weber  

Any regular unit name can be used with a SI prefix. The following table defines <short-SI-prefix> and <full-SI-name> (all names are case sensitive):

SI prefixes
Short Full Multiplicand Comments
Y yotta 1024  
Z zetta 1021  
E exa 1018  
P petta 1015  
T tera 1012  
G giga 109  
M mega 106  
k kilo 103  
h hecto 102  
da deka 101  
d deci 10-1  
c centi 10-2  
m milli 10-3  
µ micro 10-6 This prefix (00B516) is only recognized in Latin-1 and UTF-8. The latter additionally does Greek mu (03BC16)
n nano 10-9  
p pico 10-12  
f femto 10-15  
a atto 10-18  
z zepto 10-21  
y yocto 10-24  

Note that short prefixes are used with short names, full prefixes are used with full names. For instance, the only legal notations of km are: km, kilometer, kilometre, kilometers, kilometres. The irregular unit names (<irregular-unit>) cannot be used with SI prefixes. They are (all names are case sensitive):

Irregular unit names
Name(s) Meaning Comments
% Percent  
' Minute of arc  
" Second of arc  
°, degree, degrees Degree Ring (00B016) is recognized only In Latin-1 and UTF-8 encoded strings 
°F, Fahrenheit Degree of Fahrenheit In Latin-1 and UTF-8 encoded strings a combination of ring (00B016) and the capital letter F can be used. Additionally, in UTF-8 degree Fahrenheit is recognized (210916)
°K, Kelvin Degree of Kelvin In Latin-1 and UTF-8 encoded strings a combination of ring (00B016) and the capital letter K can be used. Names K and kelvin are regular (can be used with a SI prefix). In UTF-8 Kelvin sign (212A16) is also regular.
Å, Ångström, ångström Ångström Letters with accents are supported only in Latin-1 and UTF-8. Additionally in UTF-8 the angstrom sign (212B16) is recognized
a., acre, acres Acre  
are, ares Are  
atm, atmosphere, atmospheres Atmosphere  
barleycorn, barleycorns Barleycorn  
bbl, barrel, barrels Barrel  
BTU, Btu, btu British thermal unit  
c, carat, carats Carat  
cal, calorie, calories Calorie  
candle, candles Candle  
Celsius Degree of Celsius The short form of Celsius degree (°C) is regular and can be used with short SI prefixes. Therefore, the expression m°C is legal and means one thousandth part of °C i.e. (0.001 + 273.15) K.
ch, chain, chains Chain  
cubit, cubits Cubit  
d, day, days Day  
dyn, dyne Dyne  
ell, ells Ell  
eV Electronvolt  
f, fathom, fathoms Fathom  
foot, feet Foot Note that the commonly used abbreviation ft conflicts with metric ton: ft=femtoton=10-12kg
finger, fingers Finger  
fpm Feet per minute  
fps Feet per second  
fur, furlong, furlongs Furlong  
gal, gallon, gallons Gallon  
grain, grains Grain  
h, hour, hours Hour  
hand, hands Hand  
hectare, hectares Hectare  
hp, horsepower Horsepower  
in., inch, inches Inch  
INM International Nautical Mile  
Kcal, kcal Kilocalorie  
kgf, kilogram-force Kilogram-force  
knot, knots Knot  
lb, pound, pounds Pound  
line, lines Line  
link, links Link  
liqpt, liquidpint Liquid pint  
ly, lightyear, lightyears Lightyear  
mi, mile, miles Mile  
min, minute, minutes Minute  
mmHg Torr  
mpg Miles per gallon  
mph Miles per hour  
mps Miles per second  
nail, nails Nail  
Oe, oersted Oersted  
oz, ounce, ounces Ounce  
pace, paces Pace  
pc, parsec, parsecs Parsec  
point, points Point  
ppb Parts per billion  
ppm Parts per million  
ppt Parts per trillion  
PSI, psi Pounds per square inch  
pt, pint, pints Pint  
qt, quart, quarts Quart  
rd, rod, rods Rod  
rood, roods Rood  
rpm Revolutions per minute  
rps Revolutions per second  
sec Second Names s, second and seconds are regular (can be used with a SI prefix)
span, spans Span  
tablespoon, tablespoons Tablespoon  
teaspoon, teaspoons Teaspoon  
torr Torr  
township, townships Township  
u Unified atomic mass  
ua Astronomical unit  
wineglass, wineglasses Wineglass  
yd, yard, yards Yard  
year, years Year  

Examples:

34.5 * mm    
65·km/h   65 km/h
65km/h   65 km/h
K and 273.15   degree Celsius
yd^2   one square yard
lb·yd²/s²   use of Latin-1 superscripts
Exceptions
Constraint_Error Numeric error in unit expression
Data_Error Syntax error
End_Error There is no measure in the string
Layout_Error The value of Pointer is not in the range Source'First..Source'Last+1 
Unit_Error Illegal unit expression (like m/°C)

procedure Measures_Edit.Get_Unit
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Latin1  : Boolean := True
          );
procedure Measures_UTF8_Edit.Get_Unit
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure
          );

These procedures are restricted variant of the corresponding Gets. They recognize only measure units such as foot or meter. Irregular units and units with SI prefixes are recognized as well. No numbers or unit operations are recognized. Blanks are not skipped. This can be useful in syntax analyzers that may have different rules about operations and spaces. Such analyzer would rather use Get_Unit and then apply unit arithmetic for the operations it recognize. The meaning of the parameters is same as described for Get.

Exceptions
Data_Error Syntax error
End_Error There is no measure in the string
Layout_Error The value of Pointer is not in the range Source'First..Source'Last+1 

function Measures_Edit.Value
         (  Source : String;
            Latin1 : Boolean := True
         )  return Measure;
function
Measures_UTF8_Edit.Value
         (  Source : String
         )  return Measure;

These functions get the measure from the string Source. They are simplified versions of the corresponding Get-procedures. The whole string should be matched. Otherwise the exception Data_Error is propagated. The following exceptions are propagated out of the functions:

Exceptions
Constraint_Error Numeric error in unit expression 
Data_Error Syntax error 
End_Error There is no measure in the string Source
Unit_Error Illegal unit expression (like m/°C)

1.2.7. Conversion to String

The packages Measures_Edit and Measures_UTF8_Edit provide the following subroutines for measures output:

procedure Measures_Edit.Put
          (  Destination : in out String;
             Pointer     : in out Integer;
             Value       : Measure;
             Latin1      : Boolean  := True;
             Derived     : Boolean  := True;
             RelSmall    : Positive := Strings_Edit.MaxSmall;
             AbsSmall    : Integer  := -Strings_Edit.MaxSmall;
             Field       : Natural  := 0;
             Justify     : Strings_Edit.Alignment := Strings_Edit.Left;
             Fill        : Character := ' '
          );
procedure
Measures_UTF8_Edit.Put
          (  Destination : in out String;
             Pointer     : in out Integer;
             Value       : Measure;
             Derived     : Boolean  := True;
             RelSmall    : Positive := Strings_Edit.MaxSmall;
             AbsSmall    : Integer  := -Strings_Edit.MaxSmall
          );

These procedures place the measure specified by the parameter Value into the output string Destination. The string is written starting from Destination (Pointer). The procedure from the package Measures_Editt has the parameter Latin1, which when true, allows using of Latin-1 characters listed in the table above, otherwise the output is done in ASCII. The procedure from the package Measures_UTF8_Edit uses UTF-8 encoded characters from the table. In all cases only the code positions in the table having white background are used in output. So for example in UTF-8 for Ohm, Greek omega is used rather than the Ohm sign. The parameter Derived if true, allows  derived SI units (such as N, F etc.) to appear in the output. Only plain derived units do. The parameters RelSmall and AbsSmall specify the precision of numeric output (see Strings_Edit.Float_Edit for further information). The procedure from the package Measures_Edit has additional optional parameters Field, Justify, Fill. When the parameter Field is not zero then Justify specifies alignment and Fill is the character used for filling. When Field is greater than Destination'Last - Pointer + 1, the latter is used instead. The UTF-8 variant from the package Measures_UTF8_Edit does not have these parameters because they would be meaningless for a UTF-8 encoded output. After successful completion Pointer is advanced to the first character following the output or to Destination'Last + 1. A measure can be output in one of the following forms:

Format Measure type Example
<gain> Unshifted, dimensionless (a number) 25.7
<unit> Unshifted, Gain = 1 (a SI unit) m/s
<gain>*<unit> Unshifted 25.7·W
<gain> and <offset> Shifted, dimensionless 4.1 and 6.4
<unit> and <offset> Shifted, Gain = 1 K and 273.15
<gain>*<unit> and <offset> Shifted 35.08·K and 273.15

For instance:

Text    : String (1..80);
Pointer : Positive := 1;
. . .
Put (Text, Pointer, 25.0*N, Derived => False, AbsSmall => 0);

will put 25·kg·m/s² into the string Text starting from the position 1. The parameter Derived=false forbids use of N (newton) in the output. Otherwise the result would be 25·N. The parameter AbsSmall=0 tells that the value has the precision ±0.5·10AbsSmall-1, so only the digits before the decimal point are output. For further information about floating-point I/O see the description of the package Strings_Edit.

Exceptions
Layout_Error Pointer is not in Destination'Range or there is
no room for the output

function Measures_Edit.Image
         (  Value    : Measure;
            Latin1   : Boolean  := True;
            Derived  : Boolean  := True;
            RelSmall : Positive := Strings_Edit.MaxSmall;
            AbsSmall : Integer  := -Strings_Edit.MaxSmall
         )  return String;
function Measures_UTF8_Edit.Image
         (  Value    : Measure;
            Derived  : Boolean  := True;
            RelSmall : Positive := Strings_Edit.MaxSmall;
            AbsSmall : Integer  := -Strings_Edit.MaxSmall
         )  return String;

The functions Image convert the parameter Value to string. The parameters Latin1, Derived, RelSmall and AbsSmall have same meaning as in Put (see).

1.2.8. Handling I/O in multiple encodings

The generic package Measures_Universal_Edit provides unified subroutines for string I/O with arbitrary encodings:

generic
   with package
Irregular_Measures is new Measures_Irregular (<>);
   with package
Float_Edit is
      new
Strings_Edit.Float_Edit
          (  Irregular_Measures.Derived_Measures.Measures.Number
          );
package
Measures_Universal_Edit is ...

The package provides a set of subroutines similar to ones of Measures_Edit and Measures_UTF8_Edit. They differs from the corresponding subroutines of these package in one additional parameter Mode, which for each subroutine determines the character set to be used. 

procedure Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Mode    : Code_Set
          );

procedure Get_Unit
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Mode    : Code_Set
          );

function Image
         (  Value    : Measure;
            Mode     : Code_Set;
            Derived  : Boolean  := True;
            RelSmall : Positive := Strings_Edit.MaxSmall;
            AbsSmall : Integer  := -Strings_Edit.MaxSmall
         )  return String;

function Value
         (  Source : String;
            Mode   : Code_Set
         )  return Measure;

procedure Put
          (  Destination : in out String;
             Pointer     : in out Integer;
             Value       : Measure;
             Mode        : Code_Set;
             Derived     : Boolean  := True;
             RelSmall    : Positive := Strings_Edit.MaxSmall;
             AbsSmall    : Integer  := -Strings_Edit.MaxSmall
          );

The parameter Mode has the enumeration type Code_Set. The meaning of the values is as follows:

This package has a non-generic instance Float_Measures_Universal_Edit for the measures based on the standard type Float.


[Back][TOC][Next]

2. Packages

Two separate sets of packages implement the types Unit and the type Measure. There is also a set of supporting packages used internally.

[Back][TOC][Next]

2.1. The packages related to the type Unit

The packages related to the type Unit:

Package Provides
Units The type Unit, unit arithmetic
     Base The constants corresponding to the base SI units
Constants The constants corresponding to some physical entities
Edit Conversion to ASCII and Latin-1 strings
UTF8_Edit Conversion to UTF-8 encoded strings

[Back][TOC][Next]

2.2. The packages related to the type Measure

The following packages are related to the type Measure:

Package (generic) Provides Non-generic version (Float)
Measures The type Measure, measure arithmetic, constants corresponding to the base SI units. Float_Measures
Measures_Derived The constants corresponding to the derived SI units. Float_Measures_Derived
Measures_Edit Input and output of measures (ASCII and Latin-1 string conversions) Float_Measures_Edit
Measures_Elementary_Functions Elementary functions (sqrt, log, exp etc.) Float_Measures_Elementary_Functions
Measures_Irregular The constants corresponding to some of irregular units Float_Measures_Irregular
Measures_Universal_Edit Input and output of measures using multiple encodings Float_Measures_Universal_Edit
Measures_UTF8_Edit Input and output of measures with UTF-8 encoded strings Float_Measures_UTF8_Edit

The following example shows how the generic packages are instantiated:


with
Measures;
with Measures_Derived;
with Measures_Irregular;
with Measures_Edit;
with Strings_Edit.Float_Edit;
   . . .
type Real is digits ...;
--
-- Instantiation of Measures with the type Real as the parameter
--

package Real_Measures is new Measures (Real);
--
-- Instantiation of Measures_Derived
--

package Real_Measures_Derived is new Measures_Derived (Real_Measures);
--
-- Instantiation Measures_Irregular
--

package Real_Measures_Irregular is
   new
Measures_Irregular (Real_Measures_Derived);
--
-- Instantiation Measures_Edit
--
package Real_Edit is new Strings_Edit.Float_Edit (Real);
--
-- Instantiation Measures_Edit
--

package Real_Measures_Edit is
   new
Measures_Derived
       (  Real_Measures_Irregular,
          Real_Edit
       );

[Back][TOC][Next]

2.3. Packages defined for Internal use

The software uses the packages Strings_edit and Tables. The following table shows the packages defined for internal use:

Package Provides
Measures_Generic_Edit Generic package for I/O of measures for all types of encodings. Measures_Edit, Measures_UTF8_Edit, Measures_Universal_Edit instantiate it.
Measures_Table_Of_Integer The table of SI prefixes (their powers) or unit operations (coded as integers). Instantiates the package Tables with Integer as the parameter.
Measures_Table_Of_Measure The table of measurement units. Instantiates the package Tables with Address as the parameter.


[Back][TOC][Next]

3. Examples

[Back][TOC][Next]

3.1. Self test

The software uses the packages Strings_edit and Tables. The following table shows the packages defined for internal use: 

The file Measures_Test.adb contains a test program for the packages Measures and Units. It also estimates the performance hit when the type Measure is used for calculations instead of Float.

[Back][TOC][Next]

3.2. Unit converter

The subdirectory Units-Examples→Units-Converter contains a simple sample program which converts dimensioned values.

3.2.1. Unit converter for Win32

The following is the full source code of the converter. It is located in Units-Examples→Units-Converter→Win32. The fragments relevant to dealing with units are highlighted yellow. The rest is dealing with Windows API. The program uses Windows Unicode support. For this measures input and output is performed in UTF-8 format using the package Float_Measures_UTF8_Edit. Note that Windows uses USC-2 encoding therefore all strings are converted from Wide_String to UTF-8 and back using subroutines of the package Strings_Edit.UTF8.Handling.

Implementation, file units_converter.adb:

with Ada.IO_Exceptions; use Ada.IO_Exceptions;
with
Ada.Unchecked_Conversion;
--
-- Things related to Win32-API
--
with Win32;           use Win32;
with
Win32.WinBase;   use Win32.WinBase;
with
Win32.WinDef;    use Win32.WinDef;
with
Win32.WinMain;   use Win32.WinMain;
with
Win32.WinUser;   use Win32.WinUser;
with
Win32.CommCTRL;  use Win32.CommCTRL;
with Float_Measures;              use Float_Measures;
with Float_Measures_UTF8_Edit;    use Float_Measures_UTF8_Edit;
with
Strings_Edit.UTF8.Handling;  use Strings_Edit.UTF8.Handling;
with Units;                       use Units;
with Interfaces.C;
with System;

procedure
Units_Converter is
  
--
   -- The following are the constants defined in the resource script. See
   -- Units_Converter.rc file.
   --
   Dialog_ID  : constant := 101;
   Input_ID   : constant := 1000;
   SI_ID      : constant := 1001;
  
Base_ID    : constant := 1002;
   Message_ID : constant := 1003;
   Go_ID      : constant := 1004;
   --
   -- Useless windows return codes
   --
  
INT_Result  : INT;
   BOOL_Result : BOOL;
   UINT_Result : UINT;
   --
   -- Dialog_Proc -- Process messages to the dialog box
   --
   --    Window  - The window (a handle to)
   --    Message - To process
   --    WPar    - Its short parameter
   --    LPar    - Its long parameter
   --
   -- Returns :
   --
   --    Message processing code (message specific)
   --
  
function Dialog_Proc
            (  Window  : HWND;
               Message : UINT;
               WPar    : WPARAM;
               LPar    : LPARAM
            )  return BOOL;
   pragma Convention (Stdcall, Dialog_Proc);
   --
   -- Dialog_Proc_Access -- Pointer to Dialog_Proc
   --
  
type Dialog_Proc_Access is access function
       
(  Window  : HWND;
           Message : UINT;
           WPar    : WPARAM;
           LPar    : LPARAM
        )  return BOOL;
   pragma Convention (Stdcall, Dialog_Proc_Access);
   --
   -- To_DLGPROC -- Conversion from Dialog_Proc_Access to DLGPROC
   --
   -- This is necessary to work around access type protection system.
   -- DLGPROC is declared so that no nested function pointer may be
   -- converted to it. So we have declared another pointer type and convert
   -- it to DLGPROC.
   --
  
function To_DLGPROC is
      new
Ada.Unchecked_Conversion (Dialog_Proc_Access, DLGPROC);
   --
   -- Get_Text -- Wrapper around windows API function GetDlgItemText
   --
   --    Window - A handle to
   --    ID     - Of the control
   --
   -- Returns :
   --
   --    The text of the control
   --
  
function Get_Text (Window : HWND; ID : UINT) return String is
     
Input : WCHAR_Array (1..256);
   begin
     
UINT_Result :=
         GetDlgItemTextW
         (  Window,
            Input_ID,
            Input (Input'First)'Unchecked_Access,
            Input'Length
         );
      return
      
  To_UTF8
         (  Interfaces.C.To_Ada (Interfaces.C.WCHAR_Array (Input))
         );
   end
Get_Text;
   --
   -- Set_Text -- Wrapper around windows API function SetDlgItemText
   --
   --    Window - A handle to
   --    ID     - Of the control
   --    Text   - To be put there
   --
  
procedure Set_Text
             (  Window : HWND;
                ID     : UINT;
                Text   : String
             )  is
      Output : WCHAR_Array :=
         WCHAR_Array (Interfaces.C.To_C (To_Wide_String (Text)));
   begin
      BOOL_Result :=
         SetDlgItemTextW
         (  Window,
            INT (ID),
            Output (Output'First)'Unchecked_Access
         );
   end
Set_Text;
   --
   -- Go -- When the Go button gets pressed
   --
   --    Window - A handle to
   --
  
procedure Go (Window : HWND) is
      Number : Measure;
   begin
      Set_Text (Window, SI_ID, "");
      Set_Text (Window, Base_ID, "");
      Set_Text (window, Message_ID, "");
      Number := Value (Get_Text (Window, Input_ID));
      Set_Text (Window, SI_ID, Image (Number));
      Set_Text (Window, Base_ID, Image (Number, Derived => False));
   exception
      when
Constraint_Error =>
         Set_Text (Window, Message_ID, "Numeric error");
      when Data_Error =>
         Set_Text (Window, Message_ID, "Syntax error");
      when End_Error =>
         Set_Text (Window, Message_ID, "Nothing recognized");
      when Unit_Error =>
         Set_Text (Window, Message_ID, "Unit error");
   end Go;
   --
   -- Dialog_Proc -- Implementation
   --
  
function Dialog_Proc
            (  Window  : HWND;
               Message : UINT;
               WPar    : WPARAM;
               LPar    : LPARAM
            )  return BOOL is
   begin
      case
Message is
         when
WM_CLOSE =>
            BOOL_Result := EndDialog (Window, 0);
         when WM_COMMAND =>
            case HIWORD (DWORD (WPar)) is
               when
BN_CLICKED =>
                  case LOWORD (DWORD (WPar)) is
                     when
Go_ID =>
                        Go (Window);
                     when others =>
                        null;
                  end case;
               when others
=>
                  null;
            end case;
         when
WM_INITDIALOG =>
            Set_Text
            (  Window,
               Message_ID,
               "Source: www.dmitry-kazakov.de/ada/units.htm"
            );
         when others =>
            null;
      end case;
      return
0;
   end Dialog_Proc;
   Ptr    : Dialog_Proc_Access := Dialog_Proc'Access;
   Name   : CHAR_Array := "RichEd20.dll" & CHAR'Val (0);
   Handle : HINSTANCE;
begin
  
InitCommonControls;  -- Needed for Unicode support
   Handle := LoadLibrary (Name (Name'First)'Unchecked_Access); 
   Int_Result :=
      DialogBoxParam
      (  Get_hInstance,
         PCCH (MAKEINTRESOURCE (Dialog_ID)),
         System.Null_Address,
         To_DLGPROC (Ptr),
         0
      );
   if 0 = FreeLibrary (Handle) then null; end if;
end Units_Converter;

3.2.2. Unit converter for GTK

GTK is a platform independent library for creating graphical applications. It is available for a wide range of platforms which includes Windows and Linux. The following is the full source code of the converter based on GTK. It is located in Units-Examples→Units-Converter→GTK. The fragments relevant to dealing with units are highlighted yellow. The rest is dealing with GTK. Note that GTK natively supports UTF-8, so the package Float_Measures_UTF8_Edit is used without any further translation.

Implementation, file units_converter.adb:

with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;
with Ada.IO_Exceptions;      use Ada.IO_Exceptions;
--
-- Things related to Gtk-API
--

with Gdk.Event;     use Gdk.Event;
with Gtk.Enums;     use Gtk.Enums;
with Gtk.Frame;     use Gtk.Frame;
with Gtk.Button;    use Gtk.Button;
with Gtk.GEntry;    use Gtk.GEntry;
with Gtk.Handlers;  use Gtk.Handlers;
with Gtk.Label;     use Gtk.Label;
with Gtk.Table;     use Gtk.Table;
with Gtk.Widget;    use Gtk.Widget;
with Gtk.Window;    use Gtk.Window;
with Gtk.Main;
with Float_Measures;            use Float_Measures;
with Float_Measures_UTF8_Edit;  use Float_Measures_UTF8_Edit;
with Units;                     use Units;
procedure Units_Converter is

   package Handlers is
      new Gtk.Handlers.Callback (Widget_Type => Gtk_Widget_Record);

   package Return_Handlers is
      new Gtk.Handlers.Return_Callback
          (  Widget_Type => Gtk_Widget_Record,
             Return_Type => Boolean
          );

   function Delete_Event
            (  Widget : access Gtk_Widget_Record'Class;
               Event  : Gdk_Event
            )  return Boolean is
   begin
      return
False;
   end Delete_Event;

   procedure Destroy (Widget : access Gtk_Widget_Record'Class) is
   begin

      Gtk.Main.Main_Quit;
   end Destroy;
--
-- Read_Only_Text -- Sunken labels
--

   type Read_Only_Text is record
      Text  : Gtk_Label;
      Frame : Gtk_Frame;
   end record;
--
-- Gtk_New -- Initializes the label
--
   procedure Gtk_New
             (  Text  : in out Read_Only_Text;
                Label : String := ""
             )  is
   begin

      Gtk_New (Text.Frame);
      Set_Shadow_Type (Text.Frame, Shadow_In);
      Gtk_New (Text.Text, Label);
      Set_Alignment (Text.Text, 0.0, 0.5);
      Add (Text.Frame, Text.Text);
   end Gtk_New;
--
-- Show -- The label
--
   procedure Show (Text : in out Read_Only_Text) is
   begin

      Show (Text.Text);
      Show (Text.Frame);
   end Show;

   Window              : Gtk_Window;
   Grid                : Gtk_Table;
   Label1              : Gtk_Label;
   Label2              : Gtk_Label;
   Label3              : Gtk_Label;
   Value_To_Convert    : Gtk_Entry;
   Value_In_SI         : Gtk_Entry;
   Value_In_Base_Units : Gtk_Entry;
   Button              : Gtk_Button;
   Message             : Read_Only_Text;
   --
   -- Go -- When the Go button gets pressed
   --
   -- Widget - The button
   --

   procedure Go (Widget : access Gtk_Widget_Record'Class) is
      Number : Measure;
   begin
      Set_Text (Value_In_SI, "");
      Set_Text (Value_In_Base_Units, "");
      Set_Text (Message.Text, "");
      Number := Value (Get_Text (Value_To_Convert));
      Set_Text (Value_In_SI, Image (Number));
      Set_Text
      (  Value_In_Base_Units,
         Image (Number, Derived => False)
      );
   exception
      when Constraint_Error =>
         Set_Text (Message.Text, "Numeric error");
      when Data_Error =>
         Set_Text (Message.Text, "Syntax error");
      when End_Error =>
         Set_Text (Message.Text, "Nothing recognized");
      when Unit_Error =>
         Set_Text (Message.Text, "Unit error");
   end Go;
begin
   --
   -- Initialization
   --
  
Gtk.Main.Init;
   --
   -- Creating the main window and handle its events
   --
   Gtk.Window.Gtk_New (Window);
   Set_Title (Window, "Unit conversion (Ada95 GTK+)");
   Return_Handlers.Connect
   (  Window,
      "delete_event",
      Return_Handlers.To_Marshaller (Delete_Event'Access)
   );
   Handlers.Connect
   (  Window,
      "destroy",
      Handlers.To_Marshaller (Destroy'Access)
   );
   Gtk.Window.Set_Border_Width (Window, 10);
   --
   -- Creating the grid, a table to align all other widgets
   --
  
Gtk_New (Grid, 3, 4, False);
   Set_Row_Spacings (Grid, 3);
   Set_Col_Spacings (Grid, 3);
   Gtk.Window.Add (Window, Grid);
   --
   -- The left column are labels
   --
   
Gtk_New (Label1, "Value to convert" & LF & "For example 23.5 bar");
   Attach_Defaults (Grid, Label1, 0, 1, 0, 1);
   Set_Justify (Label1, Justify_Right);
   Set_Alignment (Label1, 1.0, 0.5);
   Gtk_New (Label2, "SI equivalent");
   Attach_Defaults (Grid, Label2, 0, 1, 1, 2);
   Set_Alignment (Label2, 1.0, 0.5);
   Gtk_New (Label3, "Base units only");
   Attach_Defaults (Grid, Label3, 0, 1, 3, 4);
   Set_Alignment (Label3, 1.0, 0.5);
   --
   -- The central column is the edit fields
   --
  
Gtk_New (Value_To_Convert);
   Handlers.Connect
   (  Value_To_Convert,
      "activate",
      Handlers.To_Marshaller (Go'Access)
   );
   Attach_Defaults (Grid, Value_To_Convert, 1, 2, 0, 1);
   Gtk_New (Value_In_SI);
   Set_Sensitive (Value_In_SI, False);
   Attach_Defaults (Grid, Value_In_SI, 1, 2, 1, 2);
   Gtk_New (Value_In_Base_Units);
   Attach_Defaults (Grid, Value_In_Base_Units, 1, 2, 3, 4);
   --
   -- The right column is the button Go
   --
   Gtk_New (Button, " Go ");
   Handlers.Connect
   (  Button,
      "clicked",
      Handlers.To_Marshaller (Go'Access)
   );
   Attach_Defaults (Grid, Button, 3, 4, 0, 4);
   --
   -- Error messages is beneath
   --
  
Gtk_New (Message, "Source: www.dmitry-kazakov.de/ada/units.htm");
   Attach_Defaults (Grid, Message.Frame, 0, 4, 4, 5);
   --
   -- Show everything
   --
  
Show (Label1);  Show (Value_To_Convert);     Show (Button);
   Show (Label2);  Show (Value_In_SI);
   Show (Label3);  Show (Value_In_Base_Units);
   Show (Message);

   Show (Grid);
   Show (Window);
   --
   -- Enter the events processing loop
   --
   Gtk.Main.Main;
end Units_Converter;

To compile this example you will need GTK, and GtkAda bindings installed.


[Back][TOC][Next]

4. Changes log

The current version was tested with Gnat 3.15p compiler.

Changes to the version 1.8:

Changes to the version 1.7:

Changes to the version 1.6:

Changes to the version 1.5:

Changes to the version 1.4:

Changes to the version 1.3:


[Back][TOC]

5. Table of Contents

Foreword
1. Types
    1.1. The type Unit
        1.1.1. Base units
        1.1.2. Unit constants
        1.1.3. Conversion to ASCII and Latin-1 strings
        1.1.4. Conversion to UTF-8 encoded strings
        1.1.5. Design notes
    1.2. The type Measure
        1.2.1. Shifted and unshifted Measures
        1.2.2. Operations defined on Measures
        1.2.3. Unit conversions
        1.2.4. Constants
        1.2.5. Elementary functions
        1.2.6. Conversion from String
        1.2.7. Conversion to String
        1.2.8. Handling I/O in multiple encodings
2. Packages
    2.1. The packages related to the type Unit
    2.2. The packages related to the type Measure
    2.3. Packages defined for internal use
3. Examples
   3.1 Self test
   3.2 Unit converter
4. Changes log
5. Table of contents