Quantities
package from a user's perspective. It explains the main ideas behind the software and defines terms which are used later. It discusses the object types provided and describes the operations which can be performed on and with Quantities
objects during their life cycle. Finally, it is shown how a user can expand the library by introducing specific modifications to already present quantities or by adding completely new types.Quantities
package is that of a quantity. A quantity is an entity which is composed of a (numerical) value, a unit, and a dimension. Quantities are used to describe some "attribute of a phenomenon, body or substance" [1] in a quantitative way. For example, time is a particular quantity. If we perform calculations with quantities, we are engaged in quantity calculus.The quantity's unit is the standard of measurement of that quantity. For example, time is measured in seconds (s). Although the SI (Système International d'Unités) provides only a single unit for a particular quantity, quite often several units for this quantity are in practical use. For example, time is also measured in minutes (min). Some units [e.g., millisecond (ms)] contain a prefix, which defines multiples of 10 of the base unit. In quantity calculus, the quantity represents the product of the value and the attached unit. The value of a quantity can be recalculated to be measured in any of its units. This will be called "to recalculate the quantity from one of the units into another unit". As an additional feature, we will provide units with a name and a symbol for easy identification.
Each quantity is related to one or more of seven base quantities defined by the SI (Système International d'Unités). These base quantities are length, mass, time, electric current, thermodynamic temperature, amount of substance and luminous intensity. The dimension of a quantity is given by the powers to which these base quantities are raised in the relation. We will call these seven numbers the components of a quantity's dimension.
The dimension of a quantity is important in quantity calculus to define commensurability. Two quantities are commensurate if their dimensions (i.e., the seven powers as defined above) are identical. Only commensurate quantities can be assigned to each other in a calculation. For example, a product of two lengths has the dimension of an area and can be assigned to an area quantity. However, it can not be assigned to a time quantity. For non-commensurate quantities also some other operations are impossible (see below). If all seven powers are zero, we call the quantity dimensionless.
The Quantities
package provides header files and libraries which facilitate to write C++ programs incorporating quantity calculus. This is basically done by defining various types which have built-in abilities to follow and enforce the rules of quantity calculus. Thus, commensurability is tested for numerical and assignment operations, and units are taken into account. Furthermore, other operations are provided (Quantity
library). There is also a collection of often used scientific (physical) quantities (see, Physical Quantities) in the PhysicalQuantities
library, which is part of Quantities
.
Once a C++ program has declared objects with the types mentioned, they can be used to perform quantity calculus. Such an object is generally referred to as a quantity object in the following. The type of such an object is the quantity type. We will further categorize quantity objects by their mode, i.e. whether they refer to a variable, a constant, a unique constant, or an unnamed transient variable.
A variable is a quantity object with a variable value. Consequently, it can be constructed with some value, be assigned to, and the value can be changed during an operation on the variable object.
A constant is a quantity object with a value which is fixed at construction. It can neither be assigned to nor have its value changed during an operation. However, it can be used in operations which leave the value of the constant object untouched, and the result of that operation can be assigned to a variable object.
A unique constant is a quantity object which does exist only once in a program. Its value is fixed already at compile time, and there is no way to construct another instance of such a unique constant object from user code. A change of the value is not possible. However, similar to a constant object it can be used in operations which leave the unique constant object untouched.
Unnamed transient quantity objects are often internally created (generated quantity objects), for example as return values of a function. Such objects can be used in operations, but in lieu of a name within the program it is not possible to assign or change the value. Other sources of such quantity objects are certain constructor calls. If this is the constructor of a well-defined quantity, the resulting object carries full unit information. However, generated quantity objects have only limited knowledge about the unit in which the value is stored. In particular, it is assumed that the standardization factor of the unit is 1.0. Thus, generated quantity objects should be used with great care (see below).
QuantityCluster objects are aggregates of simple quantity objects. In particular, the library implements variable vector, variable tuple, and variable vector tuple objects.
A variable vector object aggregates several variable objects of the same type (homogeneous aggregate). It is intended to avoid some overhead of collecting the various objects in a vector of variable objects.
A variable tuple object aggregates several variable objects of possibly different types (heterogeneous aggregate). It provides an ordered collection of these objects.
A variable vector tuple object aggregates several variable vectors in a certain order.
Quantities
package are discussed here only briefly. A more detailed technical description can be found elsewhere for Quantity and PhysicalQuantities types.Dimensions
Units - NonPrefixable, Prexiable, Prefixed, Compound, Composed
Quantities
A quantity stores its value in a particular Unit, the "storage unit". This Unit's type is incorporated into the Quantity's type. Various types of a Quantity with different storage units are derived from the same parent quantity.
Moreover, all Quantity types derived from a particular parent quantity are characterized by a "standard unit". Each Quantity provides methods to recalculate its value into the standard unit, and from the standard unit into the storage unit. This unit is used as a standard for the recalculation between different quantities derived from the same parent quantity.
Finally, each parent quantity defines a "default unit". This default unit is used as the storage unit, if the latter is not provided when the Quantity is defined (as a template, see below).
Several Variable, Constant, and UniqueConstant types may be derived from a common parent quantity class. For example, Time
, MinuteTime
, and TimeConstant
have the same paremt quantity. As discussed below, objects of these related types can simply be converted by copy construction.
Technically most of the quantity types are typedefs of complex templates. Furthermore, the special case of a UniqueConstant is an implementation of a Singleton. According to convention, UniqueConstants are written in capital letters like macro constants.
PhysicalQuantities
Quantities
package is installed according to the 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 path-to-your-install-directory/Quantities
It is usually best to include header file Quantities.h
as
#include "Quantities.h"
This directive includes all header files from the Quantities
package.
If you want to include only some of the header files, use
#include "Quantity/UnitPrefix.h"
#include "Quantity/Unit.h"
#include "Quantity/Dimension.h"
#include "Quantity/Quantity.h"
#include "Quantity/Generic.h"
#include "Quantity/Variable.h"
#include "Quantity/Constant.h"
#include "Quantity/UniqueConstant.h"
#include "PhysicalQuantities/PhysicalQuantities.h"
to include header file UnitPrefix.h
, Unit
,h, Dimension.h
, Quantity.h
, Generic.h
, Variable.h
, Constant.h
, UniqueConstant.h
, and PhysicalQuantities.h
respectively. This is only recommended for experienced users, and may be important if you want to override some of the definitions in the header files. Also, in the same way, only specific physical quantity header files, e.g. Time.h
, can be included, by using e.g.
#include "PhysicalQuantities/Time.h"
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:
If you use special constructs (see, Functions and Operations not for Direct Use) from this library, you might have to include additionalusing namespace Quantities
using
directives to your code: using namespace BSUtilities using namespace Units using namespace Dimensions
The linker must be instructed to use various libraries in the Quantities
package when linking the client program. For GNU gcc, use
Also, you must link your program against the-Lpath-to-your-install-directory/lib -lPhysicalQuantities -lQuantity
BSUtilities
library: -Lpath-to-your-BSUtilities-install-directory/lib -lBSUtilities
which according to C++ rules disappeares when program flow leaves the block of code in which is it created, or on the stack (with operatorTime t; // example: default construction, 0 s
new
), e.g. In the latter case, the object is persistent. However, the client code has to make sure to explicitlyTime *t = new Time; // also calls default constructor
delete
the object later when it is no longer needed, in order to avoid memory leaks (see Destruction of Quantity Objects).
Of course, static
quantity objects are also possible (except unique constant objects).
A quantity object (except UniqueConstant objects) can be constructed in several ways:
Time
is a time quantity with storage type double and storage unit seconds, the following declaration constructs a variable quantity object with the name t and the value 0.0 s: Time t;
This constructs an object with the value given as parameter, which is assumed to be in the storage unit of the quantity.Time t(5.0); // t = 5 s
This constructs an object with the value given as parameter, which is assumed to be in the unit given as the second parameter. In contrast to the previous constructor call, this makes the storage unit explicit.Time t(5.0, Second ()); // t = 5 s, as above
This constructs an object with the value given as parameter, which is assumed to be in the unit given as the second parameter and is recalculated into the storager unit. In this construct, the type of the second parameter is tested at compile time to make sure that the unit given is a valid unit for the parent quantity. Thus,Time t(5.0, Minute ()); // t = 5 min = 300 s
is invalid code and should be rejected by the compiler. Note, that throughout this document, we denote invalid code by "ERROR".Time t(5.0, Metre ()); // ERROR
This constructs an object with the value given as parameter, which is assumed to be in the unit given as the unit symbol in the second parameter. Again, recalculation occurs. In this construct, the type of the second parameter can not be tested at compile time. However, at run time, the character constant provided is tested versus the symbols of all available units for the parent quantity. If it is not found in the list of these unit symbols, aTime t(5.0, "min"); // t = 5 min = 300 s, as above
UnitMismatch
exception is thrown. Thus, would result in such an exception.Time t(5.0, "m"); // exception at run time
This constructor works simular to the previous one with a value and a character constant as parameters.std::string str ("min"); // define string Time t(5.0, str); // t = 5 min = 300 s, as above
Time t (1.0); // construction of source Time t1 (t); // copy construction of target, t1 has // properties identical to t
This copy constructor acts as a conversion between quantity objects of different type, as long as they are ralated to the same parent quantity.MinuteTime t (1.0, Minute ()); // construction, 1 min Time t1 (t); // copy construction, t1 stores 60 s
In the last line of this example the expression in parentheses returns an unnamed transient quantity object. Commensurability in this case is tested by comparing the dimensions of the generated quantity and the target quantity. As long as the two quantities are commensurate, the client code does not need to care about the resulting conversions.Time t (1.0); // construction Time t1 (2.0); // construction Time t2 (3.0); // construction Time t3 (t * t1 / t2); // copy construction, t3 stores 2/3 s
Quantities
package, and yields a compile time error: In special cases, however, some quantity types may define the corresponding conversion. For example, conversion between different types of temperature (ThermodynamicTemperature, CelsiusTemperature, FarenheitTemperature) are allowed:Time t (1.0); // construction Length l (t); // ERROR
ThermodynamicTemperature T (298.0);// construction, 298 K CelsiusTemperature T1 (T); // copy construction, 25 oC
For objectTime t (1.); // construction of a variable quantity object TimeConstant t1 (t); // t1 is a constant quantity object with // value 1 s.
t1
now all restrictions of a constant quantity object apply. Such unnamed transient quantity objects can also be used to print some value (for the use ofTime t (1.0); // construction, 1 s // ... t = Time (5.); // assignment of unnamed object to t, // contains now 5 s
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):
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.AmountOfSubstance m(1., Mole ()); // construction, 1 mol ElectricCharge Q = m * FARADAYCONSTANT; // multiply by F = 96487 C/mol // and get an ElectricCharge
For example, you can assign time quantities to each other, but not a length to a time:
In particular, assignment from an unnamed transient quantity object is possible if the source and the target quantity object are commensurate: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, incommensurate quantities
Assignment can be chained, and it is possible to assign a value in one statement to more than one target: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
A std::string can also be assigned to a variable. This is described below (see Input Operations with Quantity Objects).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
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. A complete list of these functions is given below (it is assumed in the examples that object t1 is dimensionless, and has a storage type double):Time t (1.0); // constructor, 1 s Frequency omega (10.0); // constructor, 10 Hz double t_result = log10 (omega * t); // for multiplication, // see below, // t_result: 1.0
operation | example function call | comments |
exp |
| calculate the exponential of the quantity object value |
log |
| calculate the natural logarithm of the quantity object value |
log10 |
| calculate the logarithm (base 10) of the quantity object value |
sin |
| calculate the sine of the quantity object value it is assumed that the value is in rad |
cos |
| calculate the cosine of the quantity object value it is assumed that the value is in rad |
tan |
| calculate the tangent of the quantity object value it is assumed that the value is in rad |
sinh |
| calculate the hyperbolic sine of the quantity object value it is assumed that the value is in rad |
cosh |
| calculate the hyperbolic cosine of the quantity object value it is assumed that the value is in rad |
tanh |
| calculate the hyperbolic tangent of the quantity object value it is assumed that the value is in rad |
asin |
| calculate the arc sine of the quantity object value it is assumed that the value is in rad |
acos |
| calculate the arc cosine of the quantity object value it is assumed that the value is in rad |
atan |
| calculate the arc tangent of the quantity object value it is assumed that the value is in rad; for the alternative atan2 function, see below. |
operation | example function call | comments |
unary+ |
| return the object itself the return quantity type is identical to that of the original object |
unary- |
| return an object with the negative value of the argument; the return quantity type is identical to that of the original object |
operator* |
| multiply by an object of storage type from the right; the return quantity type is identical to that of the original object |
operator* |
| multiply by an object of storage type from the left; the return quantity type is identical to that of the original object |
operator*= |
| multiply by an object of storage type from the right and assign to the object itself; the return quantity type is identical to that of the original object; no new object is generated |
operator/ |
| divide by an object of storage type; the return quantity type is identical to that of the original object |
operator/ |
| divide an object of storage type by quantity object; the return quantity type differs from that of the original object; the numerical result is calculated from the value in the standard unit |
operator/= |
| divide by an object of storage type and assign to the object itself; the return quantity type is identical to that of the original object; no new object is generated |
sqrt |
| calculate the square root; the return quantity type differs from that of the original object; the numerical result is calculated from the value in the standard unit; first alternative |
sqrt |
| calculate the square root; the return quantity type differs from that of the original object; the numerical result is calculated from the value in the standard unit; second alternative |
abs |
| calculate the absolute value; the return quantity type is identical to that of the original object; the old C style fabs() function is not provided, since abs() is overloaded for various data types in C++ |
ceil |
| calculate the smallest integer value not less than the argument; the return quantity type is identical to that of the original object; |
floor |
| calculate the largest integer value not greater than the argument; the return quantity type is identical to that of the original object; |
modf |
| decompose the first argument into integer and fraction parts; the return quantity type is identical to that of the original object; this quantity object contains the fraction part; the second argument must be a pointer to an object of the same quantity type as that of the first argument; this quantity object contains the integer part |
frexp |
| decompose according to the C math.h frexp function; the return quantity type is identical to that of the first parameter; this quantity object contains the normalized fraction part; the second argument must be a pointer to an object of type int; this quantity object contains the integer power of 2 |
ldexp |
| compose according to the C math.h ldexp function; the return quantity type is identical to that of the first parameter; this quantity object contains the composed quantity; the second argument must be an object of type int; this quantity object contains the integer power of 2 |
pow |
| raise the first argument to a power which is given as a rational number; in general, the return quantity type differs from that of the original object, except for Rational<1,1>; the result is calculated from the value in the standard unit. note: SquareTime is a quantity which hold squared times. |
pow |
| raise the first argument to a power which is given as a rational number, alternative formulation; in general, the return quantity type differs from that of the original object, except for Rational<1,1>; the result is calculated from the value in the standard unit. |
or assigned toMinuteTime t (1.0, Minute()); // construction, 1 min Time t1 (sqrt (t) * sqrt (t)); // square of the square root // should give 1 min // 60 s returned and copied into t1 MinuteTime t2 (sqrt (t) * sqrt (t)); // return value 60 s is copied // into t2 - 1 min
a variable or constant quantity object of a commensurate type.MinuteTime t (1.0, Minute()); // construction, 1 min Time t1 = sqrt (t) * sqrt (t)); // square of the square root // should give 1 min // 60 s returned and assigned to t1 MinuteTime t2 (sqrt (t) * sqrt (t)); // return value 60 s is assigned // to t2 - 1 min
The value of 4.1 min is first recalculated into 246 s, which is then squared to get 60516 s^2. Assignment to a SquareTime (which is in s^2) results in exactly this value, while assignment to SquareMinuteTime (in min^2) results in 50.43 min^2, which is not the expected numerical value of 4.1 x 4.1 = 16.81. However, taking the square root of both these results, we get the expected values.MinuteTime t (4.1); // construction, 4.1 min BSUtilities::Rational<long(2), long(1)> two; // define a rational number 2 SquareTime tsq = pow (t, two); // square t, tsq contains: 60516 s^2 SquareMinuteTime tsqm = pow (t, two); // square t, tsqm contains: 50.43 min^2 Time t_root = sqrt (tsq); // square root of tsq, t_root contains 246 s MinuteTime tm_root = sqrt (tsqm); // square root of tsqm, // tm_root contains 4.1 min
sqrt
and pow
functions, as well as operator/ (divide an object of storage type by quantity object).
Note also, that the pow
() functions at present can NOT be called with an integer or double value as exponent.
Among the mathematical operations linking two quantity objects, some require that the two objects are commensurate, i.e. for addition.
Chaining of this operator is possible:Time t (60.); // construction, 60 s Time t1 (10.); // construction, 10 s t += t1; // result: t is now 70 s TimeConstant tc (20.); // construction, 20 s t += tc; // result: t is now 90 s
It is, however, required that both arguments are commensurate. This is the case if they are derived from the same parent quantity,Time t (60.); // construction, 60 s Time t1 (10.); // construction, 10 s Time t2 (20.); // construction, 20 s t += t1 += t2; // result: t is now 90 s // t1 is now 30 s
in which case the result is recalculated into the unit of the left hand side argument, or if the right hand side argument is a generated quantity and the two dimensions are identical:Time t (60.); // construction, 60 s MinuteTime tm (1.); // construction, 1 min t += tm; // result: t is now 120 s
If the two argument quantities are non-commensurate, the compiler will catch this error.Time t (2.); // construction, 2 s SquareTime tsq = t * t; // construction, 4 s^2 t += tsq/t; // result: t is now 4 s t += tsq * t // ERROR
The generated quantity may be not only at the right hand side of a += operator, but also on the left hand side. This case will usually be in a chain:
Time t (2.); // construction, 2 s SquareTime tsq = t * t; // construction, 4 s^2 t += tsq/t += t; // result: t is now 6 s
Time t (60.); // construction, 60 s Time t1 (10.); // construction, 10 s t -= t1; // result: t is now 50 s TimeConstant tc (20.); // construction, 20 s t -= tc; // result: t is now 30 s Time t2 (20.); // construction, 20 s t -= t1 -= t2; // result: t is now 70 s // t1 is now -10 s MinuteTime tm (1.); // construction, 1 min t -= tm; // result: t is now 0 s
Note that this usage of the atan2 function differs from the other form (atan) in a way that in the present case, dimensioned quantity objects can be used, while this is not possible for the atan form.Time t (5.); // construction, 5 s MinuteTime tmin (0.5); // construction, 30 s double result = atan2 (t, tmin); // calculate result: // 0.165149
Either the first or the second argument can also be a generated transient quantity object. In this case, however, the dimensions of the two arguments must be commensurate. Note, that at present, it is not possible that both parameters are a generated transient quantity object.Time t (7.); // construction, 7 s Time t1 (2.); // construction, 2 s Time t2 = fmod (t, t1); // result: 1 s
Length m (10.0); // construction, 10 m Area A = m * m; // A = 100 m^2
This is even the case if the two objects are of types derived from the same parent quantity (PureNumber is a quantity object which is dimensionless):Time t (5.0); // construction, 5 s Length m (10.0); // construction, 10 m Velocity v = m/t; // v = 2 m s^-1
Time t (5.0); // construction, 5 s MinuteTime tm (1.0); // construction, 1 min PureNumber r = t/tm; // r = 12
Quantity objects which are not related by a parent quantity can not be compared. Consequently, the following is invalid:Time t (60.); // construction, 60 s MinuteTime t1 (1.); // construction, 1 min if (t == t1) // result: true // ..
However, as an exception, comparison to unnamed transient quantity objects is allowed as long as the two quantities are commensurate:Time t (60.); // construction, 60 s Length l (1.); // construction, 1 m if (t == l) // ERROR // ..
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.); // construction, 1 s Time t1 (2.); // construction, 2 s if (t == (t * t1)/t) // comparison: false // ..
The comparison operations provided by theTime 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 // ..
Quantities
package simply compare the numerical values of the two quantity objects concerned. They do not tale 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 Quantities
package does not implement any of the schemes described in these references. Thus, if necessary, this has to be included in client code.If ostringstreams (and output file streams) are to be used for output, the corresponding objects have to be constructed first:#include <iostream> Time t (1.0); // construction, 1 s cout << t; // write "1 s" to cout t >> cout; // write "1 s" to cout
This example also demonstrates that output of a quantity object can be intermixed with output of objects of other types.#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
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 commensurate object prior to output is strongly recommended:
Note in the latter, alternative formulation that the assignment has to be included into parentheses.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"
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"
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 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
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 << "xxxx s"; // 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 << "3.5 x"; // InputError thrown
For input from a string, there is an alternative formulation, which allows an assignment-type syntax: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
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 is thrown.
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.
PrintName<true>
() -- switch on output of quantity name. Subsequent output of a quantity will include the quantity object's name. Time t (1.0); // construction, 1 s t.name ("time"); // set name to "time" cout << t << endl; // prints "1 s" cout << PrintName<true> () << t << endl; // prints "time = 1 s"
PrintName<false>
() -- switch off output of quantity name. Subsequent output of a quantity will not include the quantity object's name (default). PrintSymbol<true>
() -- switch on output of quantity symbol. Subsequent output of a quantity will include the quantity object's symbol. Time t (1.0); // construction, 1 s t.symbol ("t1"); // set symbol to "t1" cout << t << endl; // prints "1 s" cout << PrintSymbol<true> () << t << endl; // prints "t1 = 1 s"
PrintSymbol<false>
() -- switch off output of quantity symbol. Subsequent output of a quantity will not include the quantity object's symbol (default).
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:
ReadName<true>
() -- switch on input of quantity name. If this control object has been used, all subsequent inputs must include a quantity name and an equal sign (default), until input of a name is explicitly switched off. Not providing a name and an equal sign will result in an InputError. Requesting the equal sign can be switched off with ReadEqual<false> (see below). Time t; // default construction, 1 s t = "2.5 s"; // input without name, t = 2.5 s cin >> ReadName<true> (); // switch input of quantity name on t = "timename = 2.5 s"; // sets also the name t = "2.5 s"; // throws InputError t = "timename 2.5 s"; // throws InputError
ReadName<false>
() -- switch off input of quantity name. Subsequent input of a quantity will not expect the quantity object's name (default). ReadSymbol<true>
() -- switch on input of quantity symbol. If this control object has been used, all subsequent inputs must include a quantity symbol and an equal sign (default), until input of a symbol is explicitly switched off. Not providing a symbol and an equal sign will result in an error. Requesting the equal sign can be switched off with ReadEqual<false> (see below). Time t; // default construction, 1 s t = "2.5 s"; // input without name, t = 2.5 s cin >> ReadSymbol<true> (); // switch input of quantity symbol on t = "timesymbol = 2.5 s"; // sets also the symbol t = "2.5 s"; // throws InputError t = "timesymbol 2.5 s"; // throws InputError
ReadSymbol<false>
() -- switch off input of quantity symbol. Subsequent input of a quantity will not expect the quantity object's symbol (default). ReadEqual<true>
() -- switch on input of quantity equal sign. Subsequent input of a quantity will expect an equal sign between the quantity object's name and symbol and the value, if name or symbol reading is switched on (default). Time t; // default construction, 1 s t = "2.5 s"; // input without name, t = 2.5 s cin >> ReadSymbol<true> (); // switch input of quantity symbol on t = "timesymbol = 2.5 s"; // sets also the symbol t = "2.5 s"; // throws InputError t = "timesymbol 2.5 s"; // throws InputError cin >> ReadEqual<false> (); // swith off request for "=" t = "timesymbol 2.5 s"; // now this is ok
ReadEqual<false>
() -- switch off input of quantity equal sign. Subsequent input of a quantity will not expect an equal sign between the quantity object's name and symbol. ReadUnit<U>
() -- set unit U to be assumed for input of a quantity. Throws anTime t; // default construction, 1 s t = "2.5"; // input without unit, t = 2.5 s cin >> ReadUnit<Minute> (); // switch interpretation to Minute t = "2.5"; // now t = 150 s
InputError
if the symbol of U is not found in the list of symbols allowed for the quantity object. Time t; // default construction, 1 s t = "2.5"; // input without unit, t = 2.5 s cin >> ReadUnit<Metre> (); // switch interpretation to Metre t = "2.5"; // throws InputError, Metre not allowed // for Time
ReadUnit<NoUnit>
() -- switch off any assumption of unit for input of a quantity. Time t; // default construction, 1 s cin >> ReadUnit<Minute> (); // switch interpretation to Minute t = "2.5"; // input without unit, t = 150 s cin >> ReadUnit<NuUnit>; // switch off interpretation as min t = "2.5"; // now t = 2.5 s
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.
You can generate a new (possibly transient) quantity object with the recalculated value:.
The two alternatives should give the same result.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
Similarly, the conversion can be triggered by a string which corresponds to a symbol string of a unit, which is associated with the parent quantity of the quantity to be converted: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
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
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 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).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
value
(unit) returns the value in unit; an object of unit is passed as argument The unit must be a valid unit of the parent quantity.Time t(60.0); // construction, 60 s double t1 = t.value (Minute ()); // t1 = 1.0 MinuteTime tmin(1.0); // construction, 1 min double tmin1 = tmin.value (Second ()); // t1 = 60.0
value
(unitsymbolstring) returns the value in unit; a string is passed as argument The string must match a unit symbol of a valid unit of the parent quantity.Time t(60.0); // construction, 60 s double t1 = t.value (Minute ()); // t1 = 1.0 MinuteTime tmin(1.0); // construction, 1 min double tmin1 = tmin.value (Second ()); // t1 = 60.0
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"
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"
symbol
() reports the symbol of the quantity object. Per default, after construction, the symbol string of the object is empty. However, the symbol may be set (see Status Change Operations with Quantity Objects).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"
name
() reports the name of the quantity object. Per default, after construction, the name string of the object is empty. However, the name may be set (see Status Change Operations with Quantity Objects).Time t(3.0); // construction, 3 s t.name ("time"); // set the name of t std::string name = t.name (); // name contains "time"
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. Note, that in contrast to all other enrteis in this section, IsDimensionless
is not a function, but a static constant. 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
name
sets the name string in a quantity object. It uses a parameter of type std::string. The 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 std::string namestring ("time"); // define a string t.name (namestring); // now t has name "time"
Time t; // default construction, 1 s t.name ("time"); // now t has name "time"
symbol
sets the symbol string in a quantity object. It uses a parameter of type std::string. The 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 std::string symbolstring ("t"); // define a string t.symbol (symbolstring); // now t has symbol "t"
Time t; // default construction, 1 s t.symbol ("t"); // now t has symbol "t"
The version
() function returns a std::string with some information about the version of the currently used implementation and the versions of some packages it depends on. 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
The following functions and operations on or with quantity objects are not recommended for direct use in user code. They are, however, frequently used in the Quantities code itself.
print (ostream)
print_value (os)
read_value (str)
FindBySymbol<>
ListUnitSymbols<>
AllUnits<>
DefaultUnit<>
GenerateVariable<>
SameDimensioned<>
CheckSecondDimension<>
CheckUnit<>
CheckUnits<>
Unit::name ()
Unit::symbol ()
Unit::is_SI ()
Unit::is_exact ()
Unit::standard () ???
Unit::reverse () ???
further helper functions in Unit and Dimension classes
new
should later be explicitly deleted: Time *t = new (Time); // ... use pointer to Time object t delete t;
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.
DimensionError<true>
should occur in the compilers output, if incommensurability of dimensions is detected.
UnitError<true>
should occur in the compilers output, if an inappropriate unit has been used. Note, that depending on the compiler used, these messages are buried in a large number of output lines, and are often not easily found.
InputError
is thrown by input functions, if the input does not conform to the requirements, e.g. the input string is too short or does not contain substrings requested.
UnitMismatch
is thrown if an operation (often a conversion) involving a unit is requested and the unit is not in the list of units available for the quantity objects type, or its parent quantity's type.
QuantityVectorOutOfBounds
(to be written)
The PhysicalQuantities
library provides implementations of various quantities, which are 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. In the present version, the quantities listed in the following table are implemented. In this table, also, the dimensions (in the form of the power septuple) and the units defined are given
quantity with parent quantity | derived quantities | dimension | unit(s) |
acceleration AccelerationQuantities | Acceleration AccelerationConstant AccelerationVector | 1, 0, -2, 0, 0, 0, 0 | Gal standard acc. of free fall metre per square second |
... | ... | ., ., ., ., ., ., . | ... |
[2] N.M. Josuttis, The C++ Standard Library. A Tutorial and Reference, Addison, Wesley Longman, Reading, 1999.