User Manual

Contents

  1. Introduction
  2. Using Quantity Objects
  3. Physical Quantities
  4. General Quantities
  5. References

Introduction

This manual describes the Quantities package from a user's perspective. It describes the operations which can be performed on and with Quantities objects during their life cycle.

Using Quantity Objects

Preparing for Use of Quantities

First, make sure that the Quantities package is installed according to the Installation instructions given elsewhere.

In order to use the classes within the Quantities package in your program, you have to (a) include header files in your client code, and (b) link the program against libraries.

For the discussion of header files, we assume that you point your compiler to use the installation directory of Quantities in the search path for header files. For example, with GNU gcc use

-I <_install_directory_>/include/Quantities-<versionnumber>

with <_install_directory_> being the installation directory (e.g., given as the --prefix option during installation of the package) and <versionnumber> being the version number of the Quantities package.

To include specific physical quantity header files, e.g. Time.h, use

 #include "Quantities/PhysicalQuantities/Time.h"

or the respective include directive. In this case, all relevant basic header files are automatically included. This is recommended for user code.

If you want to include only some of the header files, without including any PhysicalQuantities header, use

 #include "Quantities/Quantity/Prefix.h"
 #include "Quantities/Quantity/Unit.h"
 #include "Quantities/Quantity/Dimension.h"
 #include "Quantities/Quantity/Quantity.h"
 #include "Quantities/Quantity/Generic.h"
 #include "Quantities/Quantity/Variable.h"
 #include "Quantities/Quantity/Constant.h"
 #include "Quantities/Quantity/UniqueConstant.h"

to include header file Prefix.h, Unit,h, Dimension.h, Quantity.h, Generic.h, Variable.h, Constant.h, and UniqueConstant.h respectively.

If you do not want to explicitly qualify the namespace for names from the Quantities package, you may include a using directive into your code:

using namespace quantity
If you use special constructs from this library, you might have to include additional using directives to your code:
using namespace BSUtilities
using namespace unit
using namespace dimension
Declarations and definitions for PhysicalQuantities are given in separate namespaces. The name of these namespaces is xxx for PhysicalQuantity Xxx, respectively. The namespace contains the respective Dimension, Units, and Quantity definitions. Of these, user code in most cases will only need the names of the Unit classes. If you do not give a
using namespace xxx
directive (with xxx substituted by the name of the PhysicalQuantity) you have to qualify the scope if you use a unit, e.g.:
Length (1.0, length::Minute);
In the following examples, we will omit these qualifications.

As special case is namespace time. It may conflict with a name in time.h. Thus, to disambiguate, the scope qualifier quantity::time may be needed.

The linker must be instructed to use various libraries in the Quantities package when linking the client program. For GNU gcc, use

-L<_install_directory_>/lib/Quantities-<versionnumber><libtag> -lPhysicalQuantities -lQuantity 

Also, you must link your program against the BSUtilities library:
-L<_BSUtilities_install_directory_>/lib/BSUtilities-<versionnumber><libtag> -lBSUtilities

If using a dynamically linked executable, you also have to give the runtime linker enough (and consistent) information about these libraries' place in your file system. Thus, _installdir_/lib/ (and the corresponding path to the BSUtilities library must be present either in ld.so.conf or in LD_LIBRARY_PATH.

The Life Cycle of a Quantity Object

Similar to other objects in a C++ program, the life cycle of a quantity object usually starts with construction. In the special case of a UniqueConstant the corresponding object is constructed only once at the first point of use. After construction the quantity object can be assigned to (if it is a variable quantity) and it can be used in various operations. Finally, the quantity object is destructed.

Construction of Quantity Objects

During the construction of a quantity object, an initial value as well as the name and the symbol of the quantity are determined and stored.

Changes of the value of a variable object during its lifetime is performed mainly through assignment operations. The direct change by supplying a number is not possible. Assignments to (and, thus, value changes of) Constant and UniqueConstant objects are not allowed.

The initial name and symbol of a quantity are determined in the following way: for each base quantity, a default name and symbol is defined. If a derived quantity additionally defines its own name and symbol, and has the Overwrite traits set to true in the DerivedQuantityTraits class, the respective derived values are used. For Variable and Constant objects, the user can override these predefined defaults further by using a special constructor.

Time t;              // value: 0 s, name: time, symbol: t
ElectricCharge Q = ELEMENTARYCHARGE;
                     // the elementary charge uses overwriting values for name and symbol;
                     // name: elementary charge, symbol: e
                     // different from ElectricCharge objects with name: electric charge
                     // and symbol: Q 
Time t (7., "time_seven", "t_7");
                     // value:  7 s, name: time_seven, symbol: t_7

Explicit construction of a quantity object can be done as

A quantity object (except UniqueConstant objects) can be constructed in several ways:

All examples above construct a named object. However, constructors can also be used to generate transient objects, which do not have a name within the client program. Of course, the state of the transient object has to be saved to another (named) object by copy construction or assignment, before it can be used. For example,
 Time t = Time (5.);         // copy construction by unnamed object
                             // t now contains now 5 s
 Time t1 (5.);               // t1 = 5 s; preferred
Since the above code generates a (superfluous) temporary object, and destroys it immediately after copying to t (see also [Dewhurst_2003]), it should possibly be avoided, and direct construction, as for t1 above should be preferred.
Such unnamed transient quantity objects can, however, also be used to print some value (for the use of operator<< see Input and Output Operations with Quantity Objects):
  std::cout << Time (5.) << std::endl; // prints "5 s"

Variable and constant quantity objects can be used after construction in the C++ client code just as built-in types.

Unique constant objects do not need to be constructed explicitly. They are generated at first use. Consequently, you can just write the name of a unique constant in an expression (for assignment in the second line of code in this example, see section Assignment Operations with Quantity Objects):

 AmountOfSubstance m(1., Mole ());        // construction, 1 mol
 ElectricCharge Q = m * FARADAYCONSTANT;  // multiply by F = 96487 C/mol
                                          // and get an ElectricCharge
These two lines first construct an AmountOfSubstance object m, with contents 1 mol. Then, m is multiplied by the Faraday constant to yield an electric charge, in the present case this should be 96487 C.

Conversion Operations between Quantity Objects

... definition of conversion: different storage type, different storage unit; different quantity type only in special cases ...

... conversion by copy construction ...

... conversion by assignment ...

... conversion in special cases ...

... check (copy) construction discussions above !!!!! ...

Assignment Operations with Quantity Objects

With an assignment operation you change the value of an already existing variable quantity object (target) using another object (source).

Of course, constant and unique constant quantity objects can not be target objects (see for one exception for constant objects, below). On the other hand, it is possible to assign to a variable quantity object from a quantity object in any other mode.

The following types of source objects are allowed:

The following types of source objects are prohibited:

There is one strict prerequisite which must be met for assignment: the source and target quantity object must be commensurable. Attempted assignment between incommensurable quantities yields a compile time error.

For example, you can assign time quantities to each other, but not a length to a time:

 Time t1 (1.0);       // construction, value 1 s
 Time t2;             // default construction, value 0 s
 t2 = t1;             // assignment, now t2 has value 1 s
 MinuteTime t3 (1.0); // construction, value 1 min
 t2 = t3;             // assignment, now t2 has value 60 s
 Length l (5.0)       // construction, value 5 m
 t2 = l;              // ERROR, incommensurable quantities
Assignment from an unnamed transient quantity object is possible if the source and the target quantity object are commensurable:
 Time t1 (1.0);      // construction, value 1 s
 Time t2;            // default construction, value 0 s
 t2 = (t1 * t1)/t1;  // value of t2 now 1 s   
Assignment to a constant quantity object is only possible within a statement like
 Time t (5.9);            // construction of variable time, 5.9 s
 ConstantTime tc = t;     // copy initialization
Such a statement can be regarded as initialization of the constant quantity object, and symbol = is not an assignment (see Dewhurst [Dewhurst_2003], and the precautions mentioned there).
Assignment can be chained, and it is possible to assign a value in one statement to more than one target:
  Time t1 (25.);    // construction, value 25 s
  Time t2, t3;      // default construction, both objects have value 0 s
  t3 = t2 = t1;     // now all three objects have value 25 s

Assignment from a Dynamic source object is implicitly used when assigning as in

Time t, t1 (1.0), t2 (2.0);
double exp (0.5);
t = pow ((t1 * t2), exp);             // corresponds to sqrt, but non-static exp
where the dimension of the power function result can only be determined at run-time. Note, that in general the dimension of the Dynamic can not be checked at compile-time. Consequently, incommensurable assignments can not be detected before run-time.

Among others, the following assignments are prohibited:

Time t;
t = 5.0;                              // no assignment from double
t = 5;                                // no assignment from int
t = 'a';                              // no assignment from char
BSUtilities::Rational<1,1> r;         // define a Rational<> number
t = r;                                // no assignment from Rational<>
The reason for these restrictions is that a value of a quantity itself is never meaningful. In order to interpret the value always a unit needs to be associated. The user of a quantity object is reminded of that fact when doing an assignment. If it is absolutely necessary to generate a quantity object from only a number, construction should be used.

Mathematical Operations with Quantity Objects

The Quantities package provides mathematical operations with and between quantity objects. Some of these operations concern only a single quantity object and calculate a result from it. Some other operations concern two quantity objects and the result depends on both of them. Note that bitwise operations are not provided for quantity objects.

Comparison Operations with Quantity Objects

Client programs can compare quantity objects using the conventional comparison operators == (equal), != (not qual), < (less than), > (greater than), >= (less equal), and <= (greater equal). All these operations are defined such that the values of the two objects are compared after recalculation to a common unit. Thus, comparisons are not only allowed between quantity objects of the same quantity type, but also between any object related by the same base quantity. In particular, the storage unit or the storage type may be different.
Time t (60.);                 // construction, 60 s
MinuteTime t1 (1.);           // construction, 1 min
if (t == t1)                  // result: true
  // ...
IntTime tint (7);             // construction, 7 s
if (t != tint)
  // ...                      // result: true
Quantity objects which are not related by a base quantity in general can not be compared. Consequently, the following is invalid:
Time t (60.);                 // construction, 60 s
Length l (1.);                // construction, 1 m
if (t == l)                   // ERROR
  // ...
Only if the conversion of the right hand side object in the comparison operation into an object of the left hand side type is allowed, the comparison statement is legal. Thus, the following is valid:
ThermodynamicTemperature T (273.15);        // construction, 273.15 K
CelsiusTemperature Tc (0.)                  // construction, 0 oC
if (T == Tc)                                // result: true
  // ...

Comparison to unnamed transient quantity objects is allowed as long as the two quantities are commensurable:

Time t (1.);                  // construction, 1 s
Time t1 (2.);                 // construction, 2 s
if (t == (t * t1)/t)          // comparison: false
  // ...
Furthermore, dynamic quantity objects may be compared to quantity objects:
Time t (1.);                 // construction, 1s
SquareTime tsq = t * t;      // value: 1 s^2
double power = 0.5;          // define the power (i.e., take square 
                             // root)
if (t == pow (tsq, power))   // comparison: true
  // ..

if (pow (tsq, power) == t)   // also possible
  // ..

if (pow (tsq, power) == pow (tsq, power))   // also possible
  // ..
Here, the power function returns an object of type Dynamic<ST>, which is compared to the Time object. Such a comparison only makes sense if the two objects are commensurable, i.e. have the same dimension. With dynamic quantity objects, this can only be tested at run time, which decreases performance and may compromise type safety. Thus, such constructs should be used only when dynamic access is absolutely necessary.

For comparison operations, the mode of any of the two quantity objects is not important. Thus, for example, it is allowd to compare a variable quantity onject to a constant quantity object, provided the above conditions are met:

Time t (1.0);                // construction, 1 s
TimeConstant tc (5.0);       // construction, 5 s
if (t == tc)                 // comparison: false
  // ..
LengthConstant lc (1.);      // construction, 1 m
if (t <= lc)                 // ERROR
  // ..
The comparison operations provided by the Quantities package simply compare the numerical values of the two quantity objects concerned. They do not take into account problems caused by the particular storage type of the value. For example, it is well known that comparison operations of floating point values (in particular the equality operation) can be dangerous and special precautions should be taken to avoid problems. For details, see the following links The Quantities package does not implement any of the schemes described in these references. Thus, if necessary, this has to be included in client code.

Logical Operations with Quantity Objects

In the present version, no logical operations (AND, OR, not, bitwise operations) are provided for quantity objects.

Input and Output Operations with Quantity Objects

Input and output of quantity objects into and from streams and strings is implemented. For such operations you should use operator>> and operator<<. One quantity object can be located on any side of these operators. Furthermore, a std::string can directly be assigned to a variable quantity object. The behavior of these input/output functions can be controlled by additional helper objects.

Serialization [BoostSerialization] can be also regarded as a particular type of input/output. This will be discussed in a separate section.

Unit Symbol Interpretation

The grammer which defines a well-formed unit symbol is discussed Unit Symbol Grammar elsewhere. Well-formed-ness of a symbol is only a formal requirement, it is not necessarily a symbol that is accepted for a particular quantity.

Well-formed symbols of non-prefixable and prefixed units consist of strings of alphabetical characters. In the case of composed units, the components are separated by a blank character and each component may be appended by an exponent starting with the character `^'. The exponent may be given as the quotient of two long integers, with the numerator being positive or negative, while the denominator must be positive. Zero is not allowed as one of the numbers. Numerator and enominator are separated by the character `/'. The denominator can be omitted, which is interpreted as it being 1. In addition, the numerator can be omitted, which is interpreted as the total exponent being 1.

Here are some examples of correct unit symbols:

The following examples are not well-formed:

To be acceptable as a particular quantity's unit symbol, the string must additionally be a valid unit symbol. Thus,

In some cases, there might be alternative formulations for composed units of a particular quantity. For example, the composed unit for a specific area, m^2 kg^-1 (a length unit squared divided by a mass unit), can alternatively be formulated as (m^2) kg^-1 (an area unit divided by a mass unit). These alternatives are fully equivalent for use and differ only in the representation and substitution possibilities in the graphical user interface.

Output Operations with Quantity Objects

Quantity objects of all modes can be used in output statements. Any ostream object can be used for output of a quantity. Thus, output is not only possible into one of the global ostream objects (cout, cerr, clog), but also into objects of types std::ostringstream (i.e., into a string) or std::ofstream (i.e., into a file). For usage details of these objects see any current reference to the C++ standard library, for example [Josuttis_1999]. For many of these output operations use operator << with the stream object to the left of this operator, or operator >> with the stream object to the right of this operator:
  #include <iostream>
  Time t (1.0);               // construction, 1 s
  cout << t;                  // write "1 s" to cout
  t >> cout;                  // write "1 s" to cout
If ostringstreams (and output file streams) are to be used for output, the corresponding objects have to be constructed first:
  #include <sstream>              // include header
  #include <fstream>              // include header

  std::ostringstream oss;         // construct output string stream
  Time t (5.0);                   // construction, 5 s
  oss << "a time quantity: " << t;// write text and "5 s" to stream
  std::string s (oss.str ());     // extract string from stream
                                  // and store it into s

  std::ofilestream ofs ("f.txt"); // construct output file stream
                                  // to file named f.txt
  Time t (5.0);                   // construction, 5 s
  ofs << "a time quantity: " << t;// write text and "5 s" to stream
This example also demonstrates that output of a quantity object can be intermixed with output of objects of other types.

Since unnamed transient objects do not carry information about the unit, their direct output is incomplete and contains the string "(unknown unit)". Thus, assignment to a commensurable object prior to output is strongly recommended:

  Time t (5.0);                   // construction, 5 s
  cout << (t * t)/t;              // writes "5 (unknown unit)"
  Time t1 = (t * t)/t;            // writes "5 s"
  cout << (t1 = (t * t)/t);       // alternative, writes "5 s"
Note in the latter, alternative formulation that the assignment has to be included into parentheses.

Output of quantity objects into a string can be done also directly, without using a stringstream:

  Time t (2.0);                   // construction, 2 s
  std::string s1;                 // construct string
  t >> s1;                        // s1 contains "2 s"
  std::string s2;                 // construct string
  s2 << t;                        // s2 contains "2 s"

Furthermore, a conversion of a quantity object into a std::string is provided:

  Time t (2.0);                   // construction, 2 s
  std::string s1 (t);             // construct string,
                                  // store "2 s"

Input Operations with Quantity Objects

Input operations from input streams and from strings can only be performed into variable quantity objects. Any input stream (e.g., the global cin stream, or any std::istringstram od std::ifstream) or string can be used as the source of information. For usage details of these objects see any current reference to the C++ standard library, for example Josuttis [Josuttis_1999]. Operator>> and operator<< are used to direct information into a variable quantity object placed to the right or left of the operator, respectively. The input stream or the string must be placed on the respective other side of the operator. Interpretation of the input data is based on the control objects placed in the input stream (see, Control Objects for Input and Output). Per default, it is assumed that the input stream or string contains a value and a unit symbol. The following examples use an input string stream or a string.
  Time t;                           // default construction, 1 s
  std::istringstream is ("5.0 s");  // define an input string stream
  iss >> t;                         // input from left: t = 5 s
  std::istringstream is1 ("6.0 s"); // define an input string stream
  t << iss1;                        // input from right: t = 6 s
  "7.0 s" >> t;                     // input from left: t = 7 s
  t << "8 s";                       // input from right: t = 8 s
The substring representing the value of the variable is converted into the storage type. If this conversion fails, an InputError exception (Exceptions) is thrown:
  Time t;                           // default construction, 1 s
  t << "xxxx s";                    // InputError thrown
If the unit symbol does not correspond to one of the quantity's unit's symbols, also an InputError (Exceptions) is thrown:
  Time t;                           // default construction, 1 s
  t << "3.5 x";                     // InputError thrown
If the unit is omitted in the input information, the storage unit of the variable object is assumed:
  Time t;                           // default construction, 1 s
  t << "60";                        // read without unit: t = 60 s
  MinuteTime tm;                    // default construction, 1 min
  tm << "60";                       // read without unit: tm = 60 min 
For input from a string, there is an alternative formulation, which allows an assignment-type syntax:
  Time t;                           // construction, 1 s
  t = "720 s";                      // assign, t = 720 s
  MinuteTime tm;                    // construct, 1 min
  tm = "10 min";                    // assign, tm = 10 min
  tm = "30 s";                      // re-assign, tm = 0.5 min

Note, that if input of name and/or symbol of the quantity is requested by the respective control objects (Control Objects for Input and Output), these items must be present in the input. If they are absent in these cases, an InputError exception (Exceptions) is thrown.

Control Objects for Input and Output

Control objects manipulate the behavior of the input and output functions for quantity objects. In particular, they allow to switch on and off input and output of specific pieces of information (the name and the symbol of the quantity) and (for input) to define the unit which is assumed for the interpretation of the input value. Without using the control objects, neither the name nor the symbol of the quantity object are given to the output stream or read from the input stream. Also, per default, without defining a unit for input by a control object, it is assumed that the value is given in the storage unit of the quantity object, if a unit symbol is not present in the input. If name or symbol input is requested, per default it is mandatory to place an equal sign between these strings and the quantity object's value. This behaviour can be switched off. After having switched on input or output or having defined an input unit, this state will remain in effect until it is switched off, or until another input unit is defined. If either name of symbol output is switched on, an equal sign is also placed in the output between the name and/or symbol and the value of the quantity object.

The control objects excert their function by being placed in a stream as shown by the examples below. If several output or input streams are used, they affect all streams for which they are defined. Thus, if a control object is placed in the global cout stream, it will also affect output into an output file stream. Output control objects do also have an effect on conversion of a quantity object into a std::string (see Output Operations with Quantity Objects). Similarly, if placed in cin, a control object also affects input through an input string stream or other input possibilities.

Instances of the following control objects can be placed in an output stream.

Note, that placing such objects in an std::istream will cause a compile time error.

Instances of the following control objects can be placed in a std::istream:

Note, that placing such objects in an std::ostream will cause a compile time error.

Note also, that the implementation does not distinguish between a string in the input being a name or a symbol for a quantity object. All tokens in the input will be read subsequently and interpreted in the order name, symbol, equal sign, value, and unit symbol.

Conversion Operations between Quantity Objects

Conversion between quantity objects is usually only possible if the source and target object are related to the same base quantity. One notable exception are objects of the various temperatures.
Some conversions have already been mentioned in paragraph Construction of Quantity Objects. In these cases the conversion is performed during the construction of a new quantity object. However, sometimes it is desired to convert and return the value of an existing quantity object without the overhead of creating a new quantity object.

You can generate a new (possibly transient) quantity object with the recalculated value:.

  Time t(60.0):                       // construction, 60 s
  MinuteTime tm = t (Minute ());      // return value in min, 
                                      // new transient object returned,
                                      // tm = 1.0 min
  MinuteTime tm1 (t (Minute ()));     // return value in min, 
                                      // new transient object returned,
                                      // tm1 = 1.0 min; alternative
The two alternatives should give the same result.
Another possibility is to recalculate the quantity objects value to another unit and return the resulting value:
  Time t(60.0);                       // construction, 60 s
  double t_min = t.value (Minute ()); // return value in min, 
                                      // no new object generated,
                                      // t_min = 1.0
Similarly, the conversion can be triggered by a string which corresponds to a symbol string of a unit, which is associated with the base quantity of the quantity to be converted:
  Time t(60.0);                       // construction, 60 s
  double t_min = t.value ("min");     // return value in min, 
                                      // no new object generated,
                                      // t_min = 1.0

Serialization ofQuantity Objects

... describe the use of serialization ... ... serialization of UniqueConstant is not allowed nor necessary ...

Status Report Operations with Quantity Objects

Some operations are provided in order to allow the extraction of certain pieces of information from a quantity object. It is noted that by necessity such information is incomplete as compared to the information stored in the quantity object. For example, if a value is extracted, it is just a number in the storage type without carrying information about the unit in which it is given or the dimension.

  1. value reporting operations; These operations return the value of the quantity object in the storage type without any additional information. Reporting of this number in a unit different from the storage unit as implemented in versions up to 1.2.1 is no longer possible.
    • value () returns the value in the storage unit
        Time t(1.0);                            // construction, 1 s
        double t1 = t.value ();                 // t1 = 1.0
        MinuteTime tmin(1.0);                   // construction, 1 min
        double tmin1 = tmin.value ();                 // t1 = 1.0
      

    • standard_value () returns the value in the storage unit
        Time t(1.0);                            // construction, 1 s
        double t1 = t.standard_value ();        // t1 = 1.0
        MinuteTime tmin(1.0);                   // construction, 1 min
        double tmin1 = tmin.standard_value ();  // t1 = 60.0
      
      Thus, in the second example, the contents of the MinuteTime object is 1 min, but the return value is 60 (after recalculation into the standard unit s).


  2. name/symbol reporting operations
    quantities can be interrogated for their name and symbol:
    Time t;                              // default construction
    t.name ();                           // returns the name
    t.symbol();                          // returns the symbol
    

  3. unit reporting operations There are two sets of operations which report properties of the storage unit within a quantity object or class.

    The functions in the first set, unitsymbol () and unitname (), allow dynamic access to the requested information. They can be called on a particular object, i.e. an instance of a quantity class.

    • unitsymbol () returns the string of the unit symbol
        Time t(1.0);                            // construction, 1 s
        std::string symbol = t.unitstring ();   // string contains "s"
      
    • unitname () returns the name of the unit symbol
        MinuteTime t(1.0);                  // construction, 1 s
        std::string name = t.unitname ();   // string contains "minute"
      

      The functions in the second set, Unitsymbol () and Unitname (), allow static access to the requested information. They can be called on a class name, which is known at compile time, without having to create an instance of that class.
      • Unitsymbol () returns the string of the unit symbol
          std::string symbol = Time::Unitstring ();   // string contains "s"
        
      • Unitname () returns the string of the unit symbol
          std::string name = Time::Unitname ();   // string contains "second"
        
    • operations reporting properties of the quantity
      • symbol () symbol () reports the symbol of the quantity object.
          Time t(3.0);                            // construction, 3 s
          t.symbol ("t_1");                       // set the symbol of t
          std::string symbol = t.symbol ();       // symbol contains "t_1"
        
        Per default, after construction, the symbol string of the object is pre-determined. However, the symbol may be set later in Variables and Constants (see Status Change Operations with Quantity Objects).
      • name () reports the name of the quantity object.
          Time t(3.0);                            // construction, 3 s
          t.name ("time");                        // set the name of t
          std::string name = t.name ();           // name contains "time"
        
        Per default, after construction, the name string of the object is pre-determined. However, the name may be set later in Variables and Constants (see Status Change Operations with Quantity Objects).
      • IsDimensionless provides static access to query a class whether it is dimensionless. It can be called on a class name, which is known at compile time, without having to create an instance of that class.
          bool dim = Time::IsDimensionless ();       // dim is true
        
      • isDimensionless () provides dynamic access to query a class whether it is dimensionless. It is called on an instance of the class.
          Time t(1.0);                           // construction, 1 s
          bool dim = t.isDimensionless ();       // dim is true
        

      The dynamic routines listed in this section are virtual. Thus, it is possible to generically work with pointers to a Quantities object, and still retrieve the desired information.

          Time t(1.0);                          // construction, 1 s
          ::Quantities::Quantities *q = &t;     // store generic pointer to t
          bool dim = q->isDimensionless ();     // dim is true
      

Status Change Operations with Quantity Objects

In most cases, the status of a quantity object should only be changed through the operations described above (e.g., assignment, mathematical operations). However, there are two exceptions from this rule, which refer to the setting of the name and the symbol of the quantity. These strings can be given by the default values for all modes, or set during construction for Variable and Constant objects. Furthermore, they may be set on an already existing quantity object for Variable or Constant by the following two functions:

  1. name sets the name string in a quantity object. It uses an argument of type std::string.
          Time t;                           // default construction, 1 s
          std::string namestring ("time");  // define a string
          t.name (namestring);              // now t has name "time"
    
    The implicit conversion of a character string into a std::string can be used to circumvent the explicit definition of a name string:
          Time t;                           // default construction, 1 s
          t.name ("time");                  // now t has name "time"
    

  2. symbol sets the symbol string in a quantity object. It uses an argument of type std::string.
          Time t;                           // default construction, 1 s
          std::string symbolstring ("t");   // define a string
          t.symbol (symbolstring);          // now t has symbol "t"
    
    The implicit conversion of a character string into a std::string can be used to circumvent the explicit definition of a symbol string:
          Time t;                           // default construction, 1 s
          t.symbol ("t");                   // now t has symbol "t"
    

Note that these functions are not available for a UniqueConstant quantity object.

Special Values of a Quantity Object

Four special values for each quantity type can be retrieved by the respective functions:
Time t1 = SpecialValues<Time>::unity ();    // t1 has value 1.0 s
Time t2 = SpecialValues<Time>::zero ();     // t2 has value 1.0 s
Time t3 = SpecialValues<Time>::NaN ();      // t3 has value not-a-number
Time t4 = SpecialValues<Time>::infinity (); // t4 has value infinity
The quantity type can not be a UniqueConstant.

Miscellaneous Functions and Operations on Quantity Objects

Some functions and operations which do not fit into any of the above sections are described in the following.

The version () function returns a std::string with some dynamic information about the version of the currently used implementation. For example, this information can be placed in an output stream:

  Time t(1.0);                        // default construction, 1 s
  cout << t.version () << endl;       // prints a line with version
                                      // information

... Version ... -> static info

Destruction of Quantity Objects

Quantity objects generated on the stack with operator new should later be explicitly deleted:
 Time *t = new (Time); 
 // ... use pointer to Time object t
 delete t;

The Graphical User Interface to Quantities

The graphical user interface (GUI) to quantities is an optional feature that requires the compilation of some specific libraries. Only if this has been requested at compile time, the feature is available. Moreover, the GUI requires the Qt4 system to be available. (to be updated)

Quantity Aggregates

VariableVector and VariableVectorIterator

A variable vector can be used to store several quantity values which are expressed in the same unit. Many physical quantities define a respective vector. A variable vector behaves mostly like a std::vector, for example
TimeVector tVec;         // create an empty time variable vector, unit: second
size_t s = tVec.size (); // size s is zero
This uses the default constructor. The vector can be initialized with default values, a certain number of elements, or elements from a certain range (given by iterators) of another vector (see the reference manual).

However, storing and retrieving values from the vector requires quantity objects::

TimeVector tVec;     // create an empty time variable vector, unit: second
Time t (5.);            // value: 5 s
tVec.push_back (t);     // store the value
Conversion of quantities is perfomed during storage:
MinuteTime tMin (1.0);  // value: 1 min = 60 s
tVec.push_back (tMin);  // second element in vector has value 60 s
Non-quantity values or non-convertible quantities can not be used to store elements of a variable vector:
tVec.push_back (20.);   // ERROR
Length l (10.);
tVec.push_back (l);     // ERROR, however:
CelsiusTemperatureVector tempVec;    // empty vector
ThermodynamicTemperature T (298.15);
tempVec.push_back (T);               // first element of vector is 0 degree
                                     // celsius

Data can be retrieved from the variable vector either with array style access or by iterator access:

std::cout << tVec[0];      // array style, prints 5 s
std::cout << *tVec.begin() // tVec.begin() is an iterator pointing to
                           // the first element, prints 5 s
To convert values into another unit, the return from the value has to be assigned to a quantity or used in a copy construction:
MinuteTime tMin1;
tMin1 = *(tVec.begin()+1);            // stores 1 min in tMin1
MinuteTime tMin2 (*(tVec.begin()+1)); // tMin2 is also 1 min

The last two example code lines also show the use of an iterator pointing into a variable vector. Normal, constant, reverse and constant reverse iterators are provided. They behave mostly like the respective iterators to a std::vector. However, dereferencing a variable vector iterator does not result in a reference to an alement of the vector, but rather to an additional variable object. Thus, any operation on the object returned when dereferencing a variable vector iterator hould be avoided:

TimeVector::iterator it = tVec.begin();  // points to first element 
std::cout << *it;                        // prints 5 s
Time avoid (10.);                        // avoid holds 10 s
*it = avoid;                             // does not affect the variable vector
                                         // and should be avoided

Variable vectors can be compared. Comparison takes into account possible conversions. Thus, an element `1 min' is equal to an element `60 s'. Variable vectors are compared element by element, they differ if they differ by length or (if they have equal length) by differences in elements.

Error Messages by the Compiler

The code of Quantities makes frequently use of metaprogramming techniques. Thus, the C++ compiler is internally used to perform calculations during compile time. For example, the compiler is instructed to check commensurability properties or the correctness of units. In this way, errors at run time can be minimized.

Note, that depending on the compiler used, these messages may be buried in a large number of output lines, and are often not easily found.

Exceptions

Quantities code may throw exceptions when it encounters during run time some unexpected situations. In addition, some exception-like error messages are generated during compilation (see Error Messages by the Compiler). Run time exceptions in Quantities are relatively rare since most errors are detected during compilation. However, dynamic problems may only be detected when the program runs and all information is available.

The following exceptions could be thrown:

Technically, all these exceptions (except UnitMismatch) are derived from QuantityException. It is thus possible to check for that exception type:
  Dynamic q;
  int exp;
  exp = 1;                    // no exception expected
  try {q = pow (q, exp);}     // raising to power of 1 is ok
  catch (QuantityError)
  {
// handle exception
  }
  exp = 2;                    // exception expected
  try {q = pow (q, exp);}     // dimensions are incommensurable
  catch (QuantityError)
  {
// handle exception
  }
Alternatively, the more specific exceptions can be caught:
  Dynamic q;
  int exp = 2;                // DimensionMismatch expected
  try {q = pow (q, exp);}     // dimensions are incommensurable
  catch (DimensionMismatch)
  {
// handle exception
  }
QuantityError allows to transport a textual message. This message may be set when the exception is thrown. If the code does not set the message itself, a default text is provided. The message can be used when handling the caught exception:
  try {
 // ...                       // try some code
      }
  catch (DimensionMismatch error)
  {
    std::cout << error.message () << std:: endl;
                              // print out the text message
  }

Physical Quantities

(to be finalized)

The PhysicalQuantities library provides implementations of various quantities important in scientific work, on the basis of the Quantity library. These physical quantities are selected from the SI, and the library defines the most important units, including the SI unit, and if applicable other units in common use. For details, see the PhysicalQuantities page.

General Quantities

The GeneralQuantities library provides implementations of various generally useful quantities based on the Quantity library. For details, see the GeneralQuantities page.

References

[Sutter_2005] H. Sutter and A. Alexandrescu, C++ Coding Standards, Addison-Wesley, Boston, 2005, p. 4.

[Dewhurst_2003] S.C. Dewhurst, C++ Gotchas, Addison-Wesley, Boston, 2003, p. 129, p. 153 ff.

[BoostSerialization] http://www.boost.org/doc/libs/1_35_0/libs/serialization/doc/index.html

[Josuttis_1999] N.M. Josuttis, The C++ Standard Library. A Tutorial and Reference, Addison, Wesley Longman, Reading, 1999.

back to top

back to Quantities start page


Generated on Wed Apr 11 18:07:08 2012 for Quantities by  doxygen 1.5.6