UNITS OF MEASUREMENT
FOR ADA
version 3.13
by Dmitry A. Kazakov
(mailbox@dmitry-kazakov.de)
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.
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:
See also the changes log. |
You also may wish to visit this site devoted to the problem of dimensioned values in Ada.
ARM | Intel | ||||||||
Download Units of Measurements for Ada | Platform: | 64- | 32- | 64- | 32bit | ||||
Fedora packages | precompiled and packaged using RPM | ||||||||
CentOS packages | precompiled and packaged using RPM | ||||||||
Debian packages | precompiled and packaged for dpkg | ||||||||
Ubuntu packages | precompiled and packaged for dpkg | ||||||||
Source distribution (any platform) | units_3_13.tgz (tar + gzip, Windows users may use WinZip) |
The units converter and mapper are small utilities provided as exercises to illustrate use of the software. The unit converter is a small dialog box where you can enter a dimensioned value and then convert it to SI. The unit mapper is a dialog box that converts value from one unit to another as you type it. The source code is discussed in the section 3.2.
|
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: .
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 Ada terms 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:
and the procedure:
procedure Split
( SI : Unit;
Current : out Natural;
Luminescence : out Natural;
Temperature : out Natural;
Mass : out Natural;
Length : out Natural;
Quantity : out Natural;
Time : out Natural
);
The procedure returns base unit powers of the argument SI.
The child package Units.Base defines constants for
the base SI 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;
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;
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:
Character | Latin-1 | Meaning |
2 | B216 | Power 2 |
3 | B316 | Power 3 |
· | B716 | Multiplication operator |
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:
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 |
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_; . . . |
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.
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.
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)
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 behaviour 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.
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 (non-shifted)
Here 0ºC is converted to its non-shifted 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).
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.
Few constants of the type Measure are defined in the
package Measures. They correspond to the base SI measurement units:
Constant | The base SI unit |
A | Ampere |
cd | Candela |
K | Kelvin |
kg | Kilogram |
m | Meter |
mol | Mole |
s | Second |
and dimensionless SI units:
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:
Constant | Unit | Comment |
C | Coulomb | |
Bq | Becquerel | |
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 | |
kat | katal | Catalytic activity |
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:
Constant | Unit of measurement | Definition | ||||
acre | Acre | 43_560 ft2 [2] | ||||
ang | Ångström | 1 nm [2, 5] | ||||
are | Are | 100 m2 [5] | ||||
atm | Athmosphere | 101_325 Pa | ||||
B | Bel |
|
||||
BTU | British thermal unit, international table | 1_055.056 J [6] | ||||
bar | Bar | 105 Pa [5] | ||||
barleycorn | Barleycorn |
|
||||
barn | Barn | 1 fm2 [3] | ||||
barrel | Barrel, crude oil or petroleum | 42 gal [2] | ||||
Ci | Curie | 3.7·1010 Bq [5] | ||||
cal | Calorie, international | 4.186_8 J [6] | ||||
carat | Carat | 200 mg [2] | ||||
Celsius | Degree of Celsius | 1 K shifted by 273.15K [4, 6] | ||||
ch | Chain | 66 ft [2] | ||||
cubit | Cubit | 18 inch [3] | ||||
d | Day | 60·60·24 s [5] | ||||
dB | Decibel | 5 ln10 [5] | ||||
degree | Degree (plane angle) | 2π/360 [5] | ||||
dram | Dram, international avoirdupois |
|
||||
dyn | Dyn | 10-5 N [6] | ||||
eV | Electronvolt | 1.602_18·10-19 J [5] | ||||
ell | Ell | 45 inch [3] | ||||
erg | Erg | 10-7 J [6] | ||||
Fahrenheit | Degree of Fahrenheit |
|
||||
fathom | Fathom | 6 ft [2] | ||||
finger | Finger | 41/2 inch | ||||
ft | Foot, international | 0.304_8 m = 1/3 yd [2] | ||||
fpm | Feet per minute | 1 ft/min | ||||
fps | Feet per second | 1 ft/s | ||||
fur | Furlong | 660 ft [2] | ||||
G | Gauss | 10-4 T [6] | ||||
gal | Gallon, U.S. liquid gallon | 231 inch3 [2] | ||||
gi | Gill, liquid |
|
||||
grain | Grain | 0.000_064_798_91 kg [1] | ||||
gram | Gram. The SI unit for mass is kilogram | 0.001 kg | ||||
h | Hour | 3_600 s [5] | ||||
hand | Hand | 4 inch [2] | ||||
hectare | Hectare | 102 are [5] | ||||
hp | Horsepower, metric | 735.498_8 W [6] | ||||
INM | International nautical mile | 1852 m [1, 5] | ||||
inch | Inch | 1/12 ft [2] | ||||
kcal | Kilocalorie | 103 cal | ||||
kgf | Kilogram-force | 9.806_65 N [6] | ||||
knot | Knot | 1 INM/h [5] | ||||
L | Liter | 1 dm3 [5] | ||||
league | League | 3 mi [2] | ||||
lb | Pound, international avoirdupois | 0.453_592_37 [1] | ||||
line | Line |
|
||||
link | Link | 0.66 ft [2] | ||||
ly | Light year | 9.460_73 1015 m [6] | ||||
mi | Mile, international | 5_280 ft [2] | ||||
min | Minute | 60 s [5] | ||||
min_of_arc | Minute of arc | 2π/(360·60) [5] | ||||
mpg | Miles per gallon, international mile per U.S. liquid gallon | 1 mi/gal | ||||
mph | Miles per hour, international mile per hour | 1 mi/h | ||||
mps | Miles per second, international mile per second | 1 mi/s | ||||
nail | Nail | 21/4 inch [3] | ||||
Oe | Oersted |
|
||||
oz | Ounce, international avoirdupois |
|
||||
pace | Pace | 30 inch [3] | ||||
pc | Parsec | 3.085_678 1016 m [6] | ||||
percent | Percent | 10-2 | ||||
point | Point | 0.013_837 inch [2] | ||||
ppb | Parts per billion | 10-9 | ||||
ppm | Parts per million | 10-6 | ||||
ppt | Parts per trillion | 10-12 | ||||
psi | Pounds per square inch | 1 lb/inch2 | ||||
pt | Pint, liquid |
|
||||
qt | Quart, liquid |
|
||||
R | Roentgen | 2.58·10-4 C/kg [5] | ||||
rd | Rod | 16.5 ft [2] | ||||
rood | Rood, square furlong | 1 fur2 | ||||
rpm | Revolutions per minute | 2π min-1 | ||||
rps | Revolutions per second | 2π s-1 | ||||
sec_of_arc | Second of arc | 2π/(360·60·60) [5] | ||||
span | Span | 9 inch [3] | ||||
t | Metric ton | Mg [5] | ||||
tablespoon | Table spoon |
|
||||
teaspoon | Tea spoon |
|
||||
torr | Torr (mmHg) |
|
||||
township | Township | 36 mi2 [2] | ||||
u | Unified atomic mass | 1.660_54·10-27 kg [5] | ||||
ua | Astronomical unit | 1.495_98·1011 m [5] | ||||
wineglass | Wine glass |
|
||||
yd | Yard, international | 0.914_4 m [1] | ||||
year | Year, tropical | 3.155_693 107 s [6] | ||||
1
National Bureau of Standards: "Refinement of Values for the Yard and the
Pound.", July 1, 1959 2 National Institute of Standards and Technology: "General Tables of Units of Measurement" 3 Webster's Third New International Dictionary 4 National Institute of Standards and Technology: "The NIST Reference on Constants, Units, and Uncertainty. SI Units" 5 National Institute of Standards and Technology: "The NIST Reference on Constants, Units, and Uncertainty. Units outside the SI" 6 National Institute of Standards and Technology: "Guide for the Use of the International System of Units (SI)" |
The package Float_Measures_Irregular is an instance of Measures_Irregular for the type Float.
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.
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.Measures_Of.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 package Measures_UTF8_Edit instantiates Measures_Universal_Edit in its public part under the name Universal_Edit.
The routines provided by both 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:
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 8916 | 210916 | Fahrenheit (one letter sign) |
℥ | - | E216 8416 A516 | 212516 | Ounce 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:
Short
name |
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 |
kat | katal | |
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):
Short | Full | Multiplicand | Comments |
Q | quetta | 1030 | |
R | ronna | 1027 | |
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 | |
r | ronto | 10-27 | |
q | quecto | 10-30 |
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):
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 | |
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 | |
dr, dram, drams | Dram | |
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 | |
gi, gill, gills | Gill | |
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 | |
league, leagues | League | |
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 | Additionally, in UTF-8 ounce sign is recognized (021D16) |
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
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
( Source : String;
Pointer : in out Integer;
Value : out Scaled;
Latin1 : Boolean := True
);
procedure Measures_UTF8_Edit.Get
( Source : String;
Pointer : in out Integer;
Value : out Scaled
);
These procedures input a measure in the form of a numeral multiplied by dimensioned scale. The syntax of measure and the parameters except Value are identical to corresponding procedures Get. The parameter Value has the type Scaled declared in the package Measures as follows:
type Value_Format is (Scalar, Numeric, Canonic, Jumbled);
type Scaled (Format : Value_Format := Canonic) is record
Numeral : Number'Base;
Scale : Measure;
end record;
The input may fall into one of the following categories:
An input is Numeric or Canonic when the expression's outermost dyadic operation is either multiplication or division and its left argument is a number. Examples:
Scalar values:
Numeral 2**2 + 1 5 45 45
Numeric values:
Numeral Scale 10*4 10 4 2 / 4 / 2 2 0.125 = (1/4)/2
Canonic values:
Numeral Scale -34.5 * mm -34.5 mm 3/s 3 Hz 10*4 feet 10 4 feet 1*(4 and 3) 1 4 shifted by 3 (2**2 + 1) m 5 m
Jumbled values:
km/h There is no numeral given (1 m)*s The numeral is not a number (1 m/m)*s Same as above, even though it is dimensionless it is not a number
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.
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:
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) |
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;
Field : Natural := 0;
Justify : Strings_Edit.Alignment := Strings_Edit.Left;
Fill : Character := ' '
);
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, and, additionally to them, °C. These units appear only alone or else with a numeric factor. They are not mixed with other units, like in N/s.
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 | A and 100.0 |
<gain>*<unit> and <offset> | Shifted | 35.08·A and 100.0 |
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.
The procedure factors out SI prefixes of power 10±3n. The prefix is then applied to the first unit, like for example in
25.7·km/s
Note that the parameter AbsSmall is corrected according to the prefix, so the effective small used for the output of <gain> (divided by 10±3n) is AbsSmall - 3n. The choice of the prefix is influenced by AbsSmall to avoid, when possible, representations of gain with an exponent.
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).
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.Measures_Of.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
( Source : String;
Pointer : in out Integer;
Value : out Scaled;
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.
The generic package Generic_Complex_Measures provides complex dimensioned values:
generic
with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (<>);
with package Real_Measures is new Measures (Complex_Types.Real);
package Generic_Complex_Measures is ...
The package provides the type Complex_Measure:
type Complex_Measure (SI : Unit := Units.Base.Unitless) is record
Gain : Complex;
Offset : Number'Base := 0.0;
end record;
Here Number corresponds to the floating-point type used for instantiation of the complex and dimensioned types. The package Complex_Measures is an instance of Generic_Complex_Measures for the standard type Float.
Unary operations:
function "abs" (Right : Complex_Measure) return Measure;
function "+" (Right : Complex_Measure) return Complex_Measure;
function "-" (Right : Complex_Measure) return Complex_Measure;
The absolute value of a complex number is the square root of Re2+Im2 with the same dimension.
Exponentiation:
function "**" (Left : Complex_Measure; Right : Integer) return Complex_Measure;
Multiplication:
function "*" (Left : Number'Base; Right : Complex_Measure) return Complex_Measure;
function "*" (Left : Imaginary; Right : Measure) return Complex_Measure;
function "*" (Left : Imaginary; Right : Complex_Measure) return Complex_Measure;
function "*" (Left : Complex; Right : Complex_Measure) return Complex_Measure;
function "*" (Left : Measure; Right : Complex_Measure) return Complex_Measure;
function "*" (Left : Complex_Measure; Right : Number'Base) return Complex_Measure;
function "*" (Left : Measure; Right : Imaginary) return Complex_Measure;
function "*" (Left : Complex_Measure; Right : Imaginary) return Complex_Measure;
function "*" (Left : Complex_Measure; Right : Complex) return Complex_Measure;
function "*" (Left : Complex_Measure; Right : Measure) return Complex_Measure;
function "*" (Left, Right : Complex_Measure) return Complex_Measure;
Division:
function "/" (Left : Number'Base; Right : Complex_Measure) return Complex_Measure;
function "/" (Left : Imaginary; Right : Measure) return Complex_Measure;
function "/" (Left : Imaginary; Right : Complex_Measure) return Complex_Measure;
function "/" (Left : Complex; Right : Complex_Measure) return Complex_Measure;
function "/" (Left : Measure; Right : Complex_Measure) return Complex_Measure;
function "/" (Left : Complex_Measure; Right : Number'Base) return Complex_Measure;
function "/" (Left : Measure; Right : Imaginary) return Complex_Measure;
function "/" (Left : Complex_Measure; Right : Imaginary) return Complex_Measure;
function "/" (Left : Complex_Measure; Right : Complex) return Complex_Measure;
function "/" (Left : Complex_Measure; Right : Measure) return Complex_Measure;
function "/" (Left, Right : Complex_Measure) return Complex_Measure;
Addition and subtraction:
function "+" (Left : Measure; Right : Complex_Measure) return Complex_Measure;
function "+" (Left : Complex_Measure; Right : Measure) return Complex_Measure;
function "+" (Left, Right : Complex_Measure) return Complex_Measure;
function "-" (Left : Measure; Right : Complex_Measure) return Complex_Measure;
function "-" (Left : Complex_Measure; Right : Measure) return Complex_Measure;
function "-" (Left, Right : Complex_Measure) return Complex_Measure;
Comparisons:
function "=" (Left, Right : Complex_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 : Complex_Measure; Right : Number'Base) return Complex_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.
function Conjugate (X : Complex_Measure) return Complex_Measure;
This function negates the imaginary part.
The exception Constraint_Error is raised by the operations when the corresponding numeric operation does. This behaviour 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.
function Get_Value (Value : Complex_Measure) return Complex;
The function Get_Value returns a SI equivalent of the argument:
function Get_Value_As (Value : Complex_Measure; Scale : Measure) return Complex;
The function Get_Value_As returns a numeric equivalent of the first argument
in the measurement units defined by the second argument:
function Normalize (Value : Complex_Measure) return Complex_Measure;
This function returns an unshifted measure with the numeric equivalent same as one of the argument.
function Shift (Value : Complex_Measure; Shift : Number'Base) return Complex_Measure;
This function returns the argument shifted to Shift items.
function Convert (Value : Complex_Measure; Scale : Measure) return Complex_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.
function To_Measure (Value : Complex) return Complex_Measure;
This function returns a dimensionless unshifted measure corresponding to Value.
function Re (X : Complex_Measure) return Measure;
function Im (X : Complex_Measure) return Measure;
These functions extract the real and imaginary parts of the complex number.
function Compose_From_Cartesian (Re, Im : Measure) return Complex_Measure;
function Compose_From_Cartesian (Re : Measure) return Complex_Measure;
These functions create a complex number from its real and imaginary parts. The parts must have exactly same units otherwise Units_Error is propagated.
procedure Set_Re (X : in out Complex_Measure; Re : Measure);
procedure Set_Im (X : in out Complex_Measure; Im : Measure);
These functions change an individual part of the complex number. The units must be compatible otherwise Units_Error is propagated.
function Modulus (X : Complex_Measure) return Measure;
This function returns the square root of Re2+Im2 with the same dimension. It is same as "and".
function Argument (X : Complex_Measure) return Number'Base;
function Argument
( X : Complex_Measure;
Cycle : Number'Base
) return Number'Base;;
These functions return the complex number angle. The form with Cycle specifies the angle cycle if different from 2π.
function Compose_From_Polar
( Modulus : Measure;
Argument : Number'Base
) return Complex_Measure;
function Compose_From_Polar
( Modulus : Measure;
Argument : Number'Base;
Cycle : Number'Base
) return Complex_Measure;;
These functions create a complex number from the modulus and angle. The form with Cycle specifies the angle cycle if different from 2π.
The described here packages use GtkAda, an Ada bindings to GTK+, a portable platform-independent graphical framework. To make use of this packages you need installed:
The generic package Measures_Gtk_Edit provides an interactive unit selection widget. The widget might look like:
The widget consists of:
The widget can be composed with other GTK+ widgets using standard widget packing techniques (see). It is also possibly to select a measurement unit in the form of a dialog, see the child package Measures_Gtk_Edit.Dialogs for further information.
The widget emits two signals, which are usually handled by a dialog window containing it. The dialog translates them into a response so that user could select a unit using simple button press or double click:
Selection of a unit in the tree view is intelligent. When the value in the edit field can interpreted as a numeral multiplied by a scale then the selected unit replaces the scale. If additionally the new unit is compatible with the old one, the numeral part is modified to retain the integral value. When the value in the edit field is not interpretable in the described form then the selected unit replaces it completely.
The package is declared as follows:
generic
Class_Name : String;
with package UTF8_Edit is new Measures_UTF8_Edit (<>);
package Measures_Gtk_Edit is ...
The formal parameters of the package is the name of the GTK+ class the widget will have. Class names are used in GTK+ to associate external resources like styles with the widget. The second parameter is an instance of the package Measures_UTF8_Edit.
There exists an instantiated version of Measures_Gtk_Edit for the standard floating point type: Gtk.Float_Measures_Edit..It is a child package of Gtk to make its naming consistent with the naming scheme used in GtkAda.
The package Measures_Gtk_Edit defines the following types:
type Gtk_Unit_Selection_Record is
new Gtk_Box_Record with private;
type Gtk_Unit_Selection is
access all Gtk_Unit_Selection_Record'Class;
The type
Gtk_Unit_Selection_Record is the type of the widget object. The type
Gtk_Unit_Selection is a
reference type used to deal with the widget. This is the standard scheme used in
GtkAda. One might need
Gtk_Unit_Selection_Record
only when a new widget type were derived from it. The following operations are
defined in the package:
function Filter
( Widget : Gtk_Unit_Selection_Record;
Value : Measure
) return Boolean;
This is a primitive operation of Gtk_Unit_Selection_Record. A derived type might wish to override it. The function is used to filter out the measurement units, which cannot be selected. Such units do not appear in the tree view and are not accepted as direct input. The parameter Widget is the widget object. The parameter Value is a measurement unit to check. The function returns true if Value is acceptable. The default implementation returns false if Value is incompatible with the unit constraint set on Widget. Otherwise, or when no constraint is set, the result is true.
function Get
( Widget : not null access Gtk_Unit_Selection_Record
) return Measure;
function Get
( Widget : not null access Gtk_Unit_Selection_Record
) return GLib.UTF8_String;
These functions return the currently selected or otherwise input measure. It is the content of unit entry box. The value is checked using the function Filter and Constraint_Error is propagated when it does not match. The result is either the measure or its textual equivalent.
Constraint_Error | Numeric error in unit expression or Filter returned false |
Data_Error | Syntax error |
End_Error | There is no measure selected |
Unit_Error | Illegal unit expression (like m/°C) |
function Get_Entry
( Widget : not null access Gtk_Unit_Selection_Record
) return Gtk_Entry;
This functions returns the edit field of Widget.
function Get_Tree_View
( Widget : not null access Gtk_Unit_Selection_Record
) return Gtk_Tree_View;
This functions returns the tree view field of Widget.
function Get_Type return Gtk_Type;
This functions returns the GTK+ type of the widget.
procedure Gtk_New
( Widget : out Gtk_Unit_Selection;
Constraint : Unit;
Initial : UTF8_String := ""
);
procedure Gtk_New
( Widget : out Gtk_Unit_Selection;
Initial : UTF8_String := ""
);
These functions create a new widget. The parameter Initial specifies the initial selection. It can be any string. Note that Initial is not checked to be a valid measurement unit, neither it is passed through Filter. Instead it is accepted as-is. When Initial contains a valid measure that can be found in the tree view box, it is additionally selected there. This search is takes into account SI prefixes when measurement unit is regular. So, Initial "km" would select "m" with the SI prefix k for 103. The parameter Constraint is optional. It specifies the unit to which the widget must be constrained. When specified, the default implementation of Filter will accept only the measures compatible with Constraint. The parameter Widget is the result.
procedure Initialize
( Widget : not null access Gtk_Unit_Selection_Record'Class;
Constraint : Unit;
Initial : UTF8_String := ""
);
procedure Initialize
( Widget : not null access Gtk_Unit_Selection_Record'Class;
Initial : UTF8_String := ""
);
These procedures perform initialization of the widget. In fact Gtk_New call to them internally. Any derived type shall call to them from its Initialize.
Style properties of the selection widget. All texts appearing in the widget can be changed using the style properties of the widget. The following table summarizes them:
Name | Type | Default | Description |
acceleration | String | Acceleration | Subsection name of acceleration units |
amount | String | Amount | Subsection name of substance units |
angle | String | Angle | Subsection name of angular units |
area | String | Area | Subsection name of area units |
capacitance | String | Capacitance | Subsection name of capacitance units |
charge | String | Charge | Subsection name of charge units |
chemistry | String | Chemistry | Section name of chemistry units |
concentration | String | Concentration | Subsection name of concentration units |
conductance | String | Conductance | Subsection name of conductance units |
current | String | Current | Subsection name of current units |
electricity | String | Electricity | electricity units |
energy | String | Energy | Subsection name of energy units |
equivalent-column-title | String | SI equivalent | The title of the second column in the unit selection tree view |
flux | String | Flux | Subsection name of flux units |
force | String | Force | Subsection name of force units |
frequency | String | Frequency | Subsection name of frequency units |
geometry | String | Geometry | Section name of geometric units |
illuminance | String | Illuminance | Subsection name of illuminance units |
inductance | String | Inductance | Subsection name of inductance units |
intensity | String | Intensity | Subsection name of intensity units |
length | String | Length | Subsection name of length units |
luminance | String | Luminance | Subsection name of luminance units |
mass | String | Mass | Subsection name of mass units |
mechanics | String | Mechanics | Section name of mechanics units |
optic | String | Optic | Section name of optic units |
potential | String | Potential | Subsection name of potential units |
power | String | Power | Subsection name of power units |
power-label | String | Power | The label of the spin box |
pressure | String | Pressure | Subsection name of pressure units |
resistance | String | Resistance | Subsection name of resistance units |
temperature | String | Temperature | Subsection name of temperature units |
time | String | Time | Subsection name of time units |
unit-column-title | String | Unit | The title of the first column in the unit selection tree view. |
velocity | String | Velocity | Subsection name of velocity units |
volume | String | Volume | Subsection name of volume units |
The formal parameters of the package is the name of the GTK+ class the widget will have. Class names are using in GTK+ to associate external resources like styles with the widget. The second parameter is an instance of the package Measures_UTF8_Edit.
The child generic package Measures_Gtk_Edit.GEntry provides a widget having the functionality of a combo box for dimensioned values input. The widget is a descendant of Gtk_Entry. It also implements the GtkCellEditable interface and thus can be used with a tree view cell renderer to provide unit input in a tree view control. When focused a drop down box containing the unit selection list appears. The user can selecting a unit from the list and directly edit the content of the entry box.
generic
type Custom_Unit_Selection_Record is
new Gtk_Unit_Selection_Record with private;
package Measures_Gtk_Edit.GEntry is ...
The formal generic parameter is a unit selection widget type derived from Gtk_Unit_Selection_Record. This widget will be used within the entry. The class name of the widget is constructed from the class name specified in the actual generic parameter by adding "Entry" to it.
There exists an instantiated version of Measures_Gtk_Edit.GEntry for the standard floating point type: Gtk.Float_Measures_Entry..It is a child package of Gtk to make its naming consistent with the naming scheme used in GtkAda.
The package defines the type of the unit selection entry widget:
type Gtk_Unit_Entry_Record is
new Gtk_Entry_Record with private;
type Gtk_Unit_Entry is
access all Gtk_Unit_Entry_Record'Class;
The widget is a specialized entry. The entry is not directly editable. When user attempts to edit it a pop down window containing Custom_Unit_Selection_Record is shown. When the entry contains a text it is shown there. When the text is a valid measure it is selected in the unit selection tree. The user can change the selection or manually edit the measure. The editing is committed by pressing enter or cancelled by pressing escape keys. It is also cancelled by leaving the focus. When the editing is successful the result of becomes the new content of the entry. The following subprograms are defined in the package:
function Editing_Canceled (Widget : not null access Gtk_Unit_Entry_Record)
return Measure;
This function return true if the last editing was cancelled by user. It is used in a cell renderer when it handles editing-done signal. At this point it would take the value from the widget when Editing_Canceled returns false, and ignore it otherwise.
function Get
( Widget : not null access Gtk_Unit_Entry_Record
) return Measure;
function Get
( Widget : not null access Gtk_Unit_Entry_Record;
Scale : Measure
) return Measure;
This function parses the entry text and returns it as a measure. The result is checked against the Widget's constraint if any. Constraint_Error is propagated if unit does not match. When the parameter Scale is specified is shall have the units compatible with the Widget's constraint. Otherwise Constraint_Error is propagated. When the widget is unconstrained Scale is ignored. When the widget is constrained and Scale is specified then a numeric content of the widget is treated as a valid measure by multiplying to Scale. When the content is dimensioned, Scale is ignored. Note that the Set_Numeric has no effect on this behaviour.
Constraint_Error | Numeric error in unit expression, incompatible unit of the widget content or Scale |
Data_Error | Syntax error |
End_Error | There is no measure selected |
Unit_Error | Illegal unit expression (like m/°C) |
function Get_Constraint
( Widget : not null access Gtk_Unit_Entry_Record
) return Unit;
This function returns the current widget constraint. Constraint_Error is propagated when Widget is unconstrained.
function Get_Type return Gtk_Type;
This functions returns the GTK+ type of the widget.
procedure Gtk_New
( Widget : out Gtk_Unit_Entry_Record;
Constraint : Unit;
Initial : GLib.UTF8_String := ""
);
procedure Gtk_New
( Widget : out Gtk_Unit_Entry_Record;
Initial : GLib.UTF8_String := ""
);
These functions create a new widget. The parameter Initial specifies the initial content of the entry. It can be any string. Note that Initial is not checked to be a valid measurement unit, neither it is passed through Filter of the type Custom_Unit_Selection_Record as specified in the generic formal parameter of the package. The parameter Constraint is optional. It specifies the unit to which the widget must be constrained. When specified, the default implementation of Filter will accept only the measures compatible with Constraint. The parameter Widget is the result.
procedure Initialize
( Widget : not null access Gtk_Unit_Entry_Record'Class;
Constraint : Unit;
Initial : GLib.UTF8_String := ""
);
procedure Initialize
( Widget : not null access Gtk_Unit_Entry_Record'Class;
Initial : GLib.UTF8_String := ""
);
These procedures perform initialization of the widget. In fact Gtk_New call to them internally. Any derived type shall call to them from its Initialize.
function Is_Constrained
( Widget : not null access Gtk_Unit_Entry_Record
) return Boolean;
This function returns true if the widget is constrained to a specific unit.
function Is_Numeric
( Widget : not null access Gtk_Unit_Entry_Record
) return Boolean;
This function returns true if the widget allows numeric values when editable. This is the default.
procedure Reset_Constraint
( Widget : not null access Gtk_Unit_Entry_Record
);
This procedure removes the unit constraint if any. Note that if the drop-down window is visible, the operation will not have effect on its content.
procedure Set_Constraint
( Widget : not null access Gtk_Unit_Entry_Record;
Constraint : Unit
);
This procedure sets/replaces widget's unit constraint. The parameter Constraint is the new constraint to set. Note that if the drop-down window is visible, the operation will not have effect on its content.
procedure Set_Numeric
( Widget : not null access Gtk_Unit_Entry_Record;
Allowed : Boolean
);
This procedure controls the Widget's behaviour when the input is a plain number and the widget is constrained to non-dimensionless values. In this case specifying a plain number is allowed, otherwise such input causes unit mismatch error.
Style properties of the unit entry widget. The following table summarizes them:
Name | Type | Default | Description |
has-header | Boolean | false | True if the unit selection tree view should have column's header |
The child generic package Measures_Gtk_Edit.GEntry.Cell_Renderer provides an editable cell renderer for GTK+ tree view widget. The renderer indicates dimensioned values as fixed-point numbers in using the specified scale. When edited, the renderer drops down a unit selection list. The user can select a unit from the list and edit the value entry. The input when the dimensioned is automatically converted to the scale of the renderer. When the input contains no units there the value is assumed specified in the renderer's scale. The renderer rejects any improperly dimensioned input. The renderer class name of the widget is constructed from the class name specified in the actual generic parameter of its parent by adding "CellRenderer" to it.
generic
package Measures_Gtk_Edit.GEntry.Cell_Renderer is ...
There exists an instantiated version of Measures_Gtk_Edit.GEntry.Cell_Renderer for the standard floating point type: Gtk.Float_Measures_Cell_Renderer..It is a child package of Gtk to make its naming consistent with the naming scheme used in GtkAda.
The package defines the type of the renderer:
type Gtk_Cell_Renderer_Measure_Record is
new Gtk_Abstract_Renderer_Record with private;
type Gtk_Cell_Renderer_Measure is
access all Gtk_Cell_Renderer_Measure_Record'Class;
The following subprograms are defined:
function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
return Number;
function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
return Measure;
function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
return GValue;
function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
return UTF8_String;
These functions are used to query the rendered value. It can be obtained as:
function Get_Type return Gtk_Type;
This functions returns the GTK+ type of the renderer.
procedure Gtk_New
( Cell : out Gtk_Cell_Renderer_Measure;
Scale : UTF8_String := "";
After : Natural := 0;
);
This procedure creates a new renderer. The result is returned through the parameter Cell .The parameter Scale determines the dimension and the multiplier of the edited values. It must be a valid measure specification for Measures_UTF8_Edit. Further the gain of the measure has to be non-negative otherwise Constraint_Error is propagated. When Scale is specified empty, it assumed to be 1 SI. The parameter After specifies the number of decimal positions after the decimal point for the rendered values. Observe that the values are rendered in Scale.
Constraint_Error | A numeric error in unit expression of Scale. The value of Scale has non-positive gain |
Data_Error | Syntax error in Scale |
Unit_Error | Illegal unit expression (like m/°C) |
procedure Initialize
( Cell : not null access Gtk_Cell_Renderer_Measure_Record'Class;
Scale : UTF8_String := "";
After : Natural := 0;
);
This procedure is called by any derived type from its Initialize.
Constraint_Error | A numeric error in unit expression of Scale. The value of Scale has non-positive gain |
Data_Error | Syntax error in Scale |
Unit_Error | Illegal unit expression (like m/°C) |
procedure Put
( Cell : not null access Gtk_Cell_Renderer_Measure_Record;
Value : Number
);
procedure Put
( Cell : not null access Gtk_Cell_Renderer_Measure_Record;
Value : Measure
);
procedure Put
( Cell : not null access Gtk_Cell_Renderer_Measure_Record;
Value : UTF8_String
);
procedure Put
( Cell : not null access Gtk_Cell_Renderer_Measure_Record;
Value : GValue
);
These functions are used to set the rendered value. It can be one of the following types:
Constraint_Error | A numeric error in unit expression |
Data_Error | Syntax error |
Unit_Error | Illegal unit expression (like m/°C), incompatible unit |
Properties of the renderer. The following table summarizes them:
Name | Type | Default | Description |
after | UInt | 0 | Digits after decimal point |
scale | string | - | The text representing the scale of the rendered values. When empty the scale is 1 SI |
text | string | - | The text representing the rendered value including the scale |
value | GDouble | - | Scaled number, the value rendered |
The child generic package Measures_Gtk_Edit.Dialogs provides a simple way to query for a measurement unit from a dialog box:
generic
type Custom_Unit_Selection_Record is
new Gtk_Unit_Selection_Record with private;
package Measures_Gtk_Edit.Dialogs is ...
The formal generic parameter is a unit selection widget type derived from Gtk_Unit_Selection_Record. This widget will be used within the dialog. The package provides the following subprograms:
function Get
( Constraint : Unit;
Initial : GLib.UTF8_String := "";
Title : GLib.UTF8_String := "Unit selection";
Confirm : GLib.UTF8_String := "gtk-ok";
Cancel : GLib.UTF8_String := "gtk-cancel";
Parent : Gtk_Window := null;
Flags : Gtk_Dialog_Flags := Modal;
Missing : GLib.UTF8_String := "No unit specified";
Erroneous : GLib.UTF8_String := "Illegal unit";
Incompatible : GLib.UTF8_String := "Incompatible unit"
) return GLib.UTF8_String;
function Get
( ... -- Same parameters as above
) return Measure;
function Get
( ... -- Same parameters as above, without Constraint
) return GLib.UTF8_String;
function Get
( ... -- Same parameters as above, without Constraint
) return Measure;
These function cause a dialog to appear. The dialog has the unit selection widget in it and up to two buttons. A function does not return until the dialog closes. When the user presses the OK button, which label is specified by the parameter Confirm, the selected measurement unit is checked for validity and it is the result of the function. The result is either a string or a measure. On failed checks the dialog does not end and an appropriate message text is shown as the parameters Missing, Erroneous and Incompatible specify. When the parameter Cancel is not an empty string, the Cancel button is shown. When the dialog box is closed using the window manager or the Cancel button, the functions propagate End_Error exception. The button names can refer to GTK+ stock buttons, as the default values do. Otherwise, the underline character within the name refer to the hot-key character. The parameter Constraint limits the measurement unit to a specific dimension. In the example shown on the figure it was Units.Base.Length. When Constraint is omitted, any valid measurement unit is acceptable. An additional or alternative constraint can be imposed by the type Cunstom_Unit_Selection_Record, as Filter describes. Title is the dialog window title. Parent is the parent window of. The parameter Flags is the dialog flags.
The package has an instantiated version for the standard floating-point type: Gtk.Float_Measures_Dialogs.
Two separate sets of packages implement the types Unit and the type Measure. There is also a set of supporting packages used internally.
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 |
The following packages are related to the type Measure:
Package (generic) | Provides | Non-generic version (Float) | ||
Generic_Complex_Measures | The type Complex_Measure, complex measures arithmetic. | Complex_Measures | ||
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_Gtk_Edit | GTK+ widget for measurement unit selection | Gtk.Float_Measures_Edit | ||
Dialogs | GTK+ dialogs for measurement unit selection | Gtk.Float_Measures_Dialogs | ||
GEntry | GTK+ widget for editing units with the functionality of a combo box | Gtk.Float_Measures_Entry | ||
Cell_Renderer | Tree view cell renderer for dimensioned value | Gtk.Float_Measures_Cell_Renderer | ||
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, Float_Measures_UTF8_Edit.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
);
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. |
The subdirectory test_units contains various tests.
The file test_measures.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. With GNAT compiler the program can be built using gnatmake:
>gnatmake -I.. test_measures.adb
The file test_gtk_unit_selection.adb is a test program for GTK+. To build it one needs installed:
Provided that GtkAda contributions is placed in the directory gtkada on the same level as the root directory of this software the command line to build test_gtk_unit_selection might look under Linux as:
>gnatmake -I.. -I../../gtkada test_gtk_unit_selection.adb `gtkada-config`
Under Windows instead of automatic configuration of GtkAda paths, you should specify the install directory of GtkAda implicitly:
>gnatmake -I.. -I../../gtkada -IC:/GtkAda/include/gtkada test_gtk_unit_selection.adb
The subdirectory units-examples contains a simple sample programs which convert dimensioned values.
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 UTF-16 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 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 Ada.Unchecked_Conversion; with Interfaces.C; with System; procedure Units_Converter is -- -- Things related to Win32-API -- BN_CLICKED : constant := 0; WM_CLOSE : constant := 16#010#; WM_INITDIALOG : constant := 16#110#; WM_COMMAND : constant := 16#111#; type BOOL is new Interfaces.C.int; type DWORD is new Interfaces.C.unsigned_long; type UINT is new Interfaces.C.unsigned; type INT is new Interfaces.C.int; type WORD is new Interfaces.C.unsigned_short; type WPARAM is mod 2 ** Standard'Address_Size; type LPARAM is range -(2 ** (Standard'Address_Size - 1)) .. +(2 ** (Standard'Address_Size - 1) - 1); type LPSTR is access all Interfaces.C.char; type LPWSTR is access all Interfaces.C.wchar_t; type HWND is new System.Address; type HINSTANCE is new System.Address; type DLGPROC is access function ( Window : HWND; Message : UINT; W_Param : WPARAM; L_Param : LPARAM ) return BOOL; pragma Convention (Stdcall, DLGPROC); function DialogBoxParamW ( Instance : HINSTANCE; ID : LPWSTR; Parent : HWND; Dialog_Func : DLGPROC; Init_Param : LPARAM ) return INT; pragma Import (Stdcall, DialogBoxParamW, "DialogBoxParamW"); function EndDialog (Dialog : HWND; Result : INT) return BOOL; pragma Import (Stdcall, EndDialog, "EndDialog"); function FreeLibrary ( Module : HINSTANCE ) return BOOL; pragma Import (Stdcall, FreeLibrary, "FreeLibrary"); function HIWORD (Value : DWORD) return WORD is begin return WORD (Value / (2 ** 16)); end HIWORD; function LOWORD (Value : DWORD) return WORD is begin return WORD (Value mod (2 ** 16)); end LOWORD; function GetDlgItemTextW ( Dialog : HWND; ID : INT; Text : LPWSTR; Max_Count : INT ) return UINT; pragma Import (Stdcall, GetDlgItemTextW, "GetDlgItemTextW"); function GetLastError return DWORD; pragma Import (Stdcall, GetLastError, "GetLastError"); procedure InitCommonControls; pragma Import (Stdcall, InitCommonControls, "InitCommonControls"); function LoadLibrary (File_Name : LPSTR) return HINSTANCE; pragma Import (Stdcall, LoadLibrary, "LoadLibraryA"); function MAKEINTRESOURCEW (Value : WORD) return LPWSTR is function To_LPWSTR is new Ada.Unchecked_Conversion (WPARAM, LPWSTR); begin return To_LPWSTR (WPARAM (Value)); end MAKEINTRESOURCEW; function Get_Instance return HINSTANCE; pragma Import (C, Get_Instance, "rts_get_hInstance"); function SetDlgItemTextW ( Dialog : HWND; ID : INT; Text : LPWSTR ) return BOOL; pragma Import (Stdcall, SetDlgItemTextW, "SetDlgItemTextW"); -- -- 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 : Interfaces.C.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 (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 : Interfaces.C.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 : Interfaces.C.char_array := Interfaces.C.To_C ("RichEd20.dll"); 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; |
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 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.Label; use Gtk.Label; with Gtk.Table; use Gtk.Table; with Gtk.Widget; use Gtk.Widget; with Gtk.Window; use Gtk.Window; with Ada.Unchecked_Conversion; with Gtk.Main; with Gtk.Missed; |
with Float_Measures;
use Float_Measures; with Float_Measures_UTF8_Edit; use Float_Measures_UTF8_Edit; with Units; use Units; |
procedure Units_Converter
is -- -- 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); Text.Frame.Set_Shadow_Type (Shadow_In); Gtk_New (Text.Text, Label); Text.Text.Set_Alignment (0.0, 0.5); Text.Frame.Add (Text.Text); end Gtk_New; -- -- Show -- The label -- procedure Show (Text : in out Read_Only_Text) is begin Text.Text.Show; Text.Frame.Show; 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; -- -- Conversions of callbacks to circumvent accessibility checks -- type Local_Handler is access procedure ( Widget : not null access Gtk_Widget_Record'Class ); function "+" is new Ada.Unchecked_Conversion (Local_Handler, Cb_Gtk_Button_Void); function "+" is new Ada.Unchecked_Conversion (Local_Handler, Cb_Gtk_Entry_Void); |
-- -- Go -- When the Go button gets pressed -- -- Widget - The button -- procedure Go (Widget : access Gtk_Widget_Record'Class) is Number : Measure; begin Value_In_SI.Set_Text (""); Value_In_Base_Units.Set_Text (""); Message.Text.Set_Text (""); Number := Value (Value_To_Convert.Get_Text); Value_In_SI.Set_Text (Image (Number)); Value_In_Base_Units.Set_Text (Image (Number, Derived => False)); exception when Constraint_Error => Message.Text.Set_Text ("Numeric error"); when Data_Error => Message.Text.Set_Text ("Syntax error"); when End_Error => Message.Text.Set_Text ("Nothing recognized"); when Unit_Error => Message.Text.Set_Text ("Unit error"); end Go; |
begin -- -- Initialization -- Gtk.Main.Init; -- -- Creating the main window and handle its events -- Gtk.Window.Gtk_New (Window); Window.Set_Title ("Unit conversion (Ada95 GTK+)"); Window.On_Delete_Event (Gtk.Missed.Delete_Event_Handler'Access); Window.On_Destroy (Gtk.Missed.Destroy_Handler'Access); Window.Set_Border_Width (10); -- -- Creating the grid, a table to align all other widgets -- Gtk_New (Grid, 3, 4, False); Grid.Set_Row_Spacings (3); Grid.Set_Col_Spacings (3); Window.Add (Grid); -- -- The left column are labels -- Gtk_New (Label1, "Value to convert" & LF & "For example 23.5 bar"); Grid.Attach_Defaults (Label1, 0, 1, 0, 1); Label1.Set_Justify (Justify_Right); Label1.Set_Alignment (1.0, 0.5); Gtk_New (Label2, "SI equivalent"); Grid.Attach_Defaults (Label2, 0, 1, 1, 2); Label2.Set_Alignment (1.0, 0.5); Gtk_New (Label3, "Base units only"); Grid.Attach_Defaults (Label3, 0, 1, 3, 4); Label3.Set_Alignment (1.0, 0.5); -- -- The central column is the edit fields -- Gtk_New (Value_To_Convert); Value_To_Convert.On_Activate (+Go'Access); Grid.Attach_Defaults (Value_To_Convert, 1, 2, 0, 1); Gtk_New (Value_In_SI); Value_In_SI.Set_Sensitive (False); Grid.Attach_Defaults (Value_In_SI, 1, 2, 1, 2); Gtk_New (Value_In_Base_Units); Grid.Attach_Defaults (Value_In_Base_Units, 1, 2, 3, 4); -- -- The right column is the button Go -- Gtk_New (Button, " Go "); Button.On_Clicked (+Go'Access); Grid.Attach_Defaults (Button, 3, 4, 0, 4); -- -- Error messages is beneath -- Gtk_New (Message, "Source: www.dmitry-kazakov.de/ada/units.htm"); Grid.Attach_Defaults (Message.Frame, 0, 4, 4, 5); -- -- Show everything -- Window.Show_All; -- -- Enter the events processing loop -- Gtk.Main.Main; end Units_Converter; |
To compile this example you will need GTK+, and GtkAda bindings installed.
Unit mapper is a small GUI program that converts values from one unit to another. It is located in units-examples→units-mapper. The source code:
Implementation, file units_mapper.adb:
with
Ada.IO_Exceptions; use Ada.IO_Exceptions; -- -- Things related to Gtk-API -- with GLib; use GLib; with Gtk.Editable; use Gtk.Editable; with Gtk.Entry_Buffer; use Gtk.Entry_Buffer; with Gtk.GEntry; use Gtk.GEntry; with Gtk.Table; use Gtk.Table; with Gtk.Widget; use Gtk.Widget; with Gtk.Window; use Gtk.Window; with Ada.Unchecked_Conversion; with Gtk.Main; with Gtk.Missed; -- -- This is what we need for unit conversions -- with Gtk.Float_Measures_Entry; use Gtk.Float_Measures_Entry; with Float_Measures; use Float_Measures; with Strings_Edit.Floats; use Strings_Edit.Floats; with Units; use Units; procedure Units_Mapper is Window : Gtk_Window; Grid : Gtk_Table; From_Unit : Gtk_Unit_Entry; From_Value : Gtk_Entry; To_Unit : Gtk_Unit_Entry; To_Value : Gtk_Entry; Ignore : Boolean := False; |
Here we do standard GTK+ stuff plus the variables for from and to fields of the mapper.
Implementation, file units_mapper.adb (continued):
-- -- Conversions of callbacks to circumvent accessibility checks -- type Local_Callback is access procedure (Cell : Gtk_Editable); function "+" is new Ada.Unchecked_Conversion ( Local_Callback, Cb_Gtk_Editable_Void ); |
This piece is used to circumvent accessibility checks in order to keep the program in one file. Normally if the signal handler is declared at the library level no unchecked conversion is needed.
Implementation, file units_mapper.adb (continued):
procedure
Changed_From (Widget : Gtk_Editable) is begin if Ignore then return; end if; Ignore := True; begin To_Value.Set_Text ( Image ( Get_Value_As ( Value (From_Value.Get_Text) * From_Unit.Get, To_Unit.Get ) ) ); exception when Data_Error => To_Value.Set_Text ("Not a number"); when End_Error | Constraint_Error => To_Value.Set_Text (""); when Unit_Error => To_Value.Set_Text ("Unit error"); end; Ignore := False; end Changed_From; |
This is the procedure called when the from field gets changed. It converts it to the units of the to field and changes the corresponding entry box. The variable Ignore is used prevent an infinite recursion of reactions on changes in the entry boxes.
Implementation, file units_mapper.adb (continued):
procedure Changed_To (Widget :
Gtk_Editable)
is begin if Ignore then return; end if; Ignore := True; begin From_Value.Set_Text ( Image ( Get_Value_As ( Value (To_Value.Get_Text) * To_Unit.Get, From_Unit.Get ) ) ); exception when Data_Error => From_Value.Set_Text ("Not a number"); when End_Error | Constraint_Error => From_Value.Set_Text (""); when Unit_Error => From_Value.Set_Text ("Unit error"); end; Ignore := False; end Changed_To; |
This is the procedure called when the to field gets changed. It converts it to the units of the from field and changes the corresponding entry box.
Implementation, file units_mapper.adb (continued):
procedure Changed_Unit (Widget :
Gtk_Editable)
is begin To_Unit.Set_Text (From_Unit.Get_Text); To_Unit.Set_Constraint (From_Unit.Get.SI); Changed_From (Widget); exception when End_Error => null; end Changed_Unit; |
This is the procedure called when a unit field gets changed. It converts the from field. The to field update automatically follows per change notification.
Implementation, file units_mapper.adb (continued):
begin -- -- Initialization -- Gtk.Main.Init; -- -- Creating the main window and handle its events -- Gtk_New (Window); Window.Set_Title ("Unit mapper (Ada GTK+)"); Window.On_Delete_Event (Gtk.Missed.Delete_Event_Handler'Access); Window.On_Destroy (Gtk.Missed.Destroy_Handler'Access); Window.Set_Border_Width (10); -- -- Creating the grid, a table to align all other widgets -- Gtk_New (Grid, 2, 2, False); Grid.Set_Row_Spacings (3); Grid.Set_Col_Spacings (3); Window.Add (Grid); -- -- The left column are labels -- Gtk_New (From_Value); From_Value.Set_Tooltip_Text ("First value to convert"); Grid.Attach_Defaults (From_Value, 0, 1, 0, 1); On_Changed (+From_Value, +Changed_From'Access); Gtk_New (From_Unit); From_Unit.Set_Tooltip_Text ("First value unit"); Grid.Attach_Defaults (From_Unit, 1, 2, 0, 1); On_Changed (+From_Unit, +Changed_Unit'Access); Gtk_New (To_Value); To_Value.Set_Tooltip_Text ("Second value to convert"); Grid.Attach_Defaults (To_Value, 0, 1, 1, 2); On_Changed (+To_Value, +Changed_To'Access); Gtk_New (To_Unit); To_Unit.Set_Tooltip_Text ("Second value unit"); Grid.Attach_Defaults (To_Unit, 1, 2, 1, 2); On_Changed (+To_Unit, +Changed_From'Access); -- -- Show everything -- Window.Show_All; -- -- Enter the events processing loop -- Gtk.Main.Main; end Units_Mapper; |
The software does not require special installation. The archive's content can be put in a directory and used as-is. For users of GNAT compiler the software provides gpr project files, which can be used in the Gnat Programming Studio (GPS).
For CentOS, Debian, Fedora, Ubuntu Linux distributions there are pre-compiled packages see the links on the top of the page.
The packages based on GtkAda require it and the GtkAda Contributions installed. When these packages are not planned for use, the corresponding failures during the installation can be safely ignored.
Project files | Provides | Use in custom project |
units | Units of measurements for Ada | with "units.gpr"; |
units-gtk | Units of measurements for Ada with GTK+ widgets. The project automatically includes the projects of GtkAda and GtkAda Contributions | with "units-gtk.gpr"; |
The following versions were tested with the compilers:
and the GtkAda versions:
Changes (22 April 2023) to the version 3.12:
Changes (26 November 2022) to the version 3.11:
Changes (22 May 2022) to the version 3.10:
Changes (16 April 2022) to the version 3.9:
Changes (31 May 2020) to the version 3.8:
The following versions were tested with the compilers:
and the GtkAda versions:
Changes (5 Aug 2018) to the version 3.7:
The following versions were tested with the compilers:
and the GtkAda versions:
Changes (25 July 2016) to the version 3.6:
The following versions were tested with the compilers:
and the GtkAda versions:
Changes (29 June 2015) to the version 3.5:
The following versions were tested with the compilers:
and the GtkAda versions:
Changes (2 April 2015) to the version 3.4:
Changes (1 June 2014) to the version 3.3:
The following versions were tested with the compilers:
and the GtkAda versions:
Changes to the version 3.2 (27 April 2013):
Changes to the version 3.1:
Changes to the version 3.0:
The following versions were tested with the compilers:
and the GtkAda:
Changes to the version 2.9.
Changes to the version 2.8.
The following versions were tested with the compilers:
and the GtkAda:
Changes to the version 2.7.
Changes to the version 2.6.
The following versions were tested with the compilers:
and the GtkAda:
Changes to the version 2.5.
The following versions were tested with the compilers:
and the GtkAda:
Changes to the version 2.4.
Changes to the version 2.3.
Changes to the version 2.2.
The following versions were tested with the compilers:
Note that GNAT GPL 2006 distribution for Windows has a bug in the installation files that prevents GtkAda programs from being linked. To fix it you should replace all GTK+ DLLs in the directory GNAT/bin with the same named DLLs from the directory GtkAda/bin.
and the GtkAda:
Changes to the version 2.1.:
The following versions were tested with the compilers:
Changes to the version 2.0.:
The following versions were 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: