Quantities
package are discussed here only briefly from a technical point of view. A more detailed description can be found elsewhere for Quantity and PhysicalQuantities types.Dimensions
Units - NonPrefixable, Prexiable, Prefixed, Compound, Composed
Quantities
The numerical value of a quantity object is stored internally in a certain appropriate type, the "storage type". The default storage type is double
. In the physical quantities (Physical Quantities) provided, the storage type is mentioned in the type name. For example, IntTime
is a type for physical quantity time with storage type int
. Physical quantities that use the default storage type do usually not have such a prefix, i.e. Time
is assumed to have double
as the storage type. Note, that the use of storage types that loose information when generated from a double (e.g., int), may result in incorrect results within 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 base quantity.
Moreover, all Quantity types derived from a particular base 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 base quantity.
Finally, each base 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 base quantity class. For example, Time
, MinuteTime
, and TimeConstant
have the same base 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 UniqueConabstract stant is an implementation of a Singleton. According to convention, UniqueConstants are written in capital letters like macro constants.
The fact that many operations done with Quantities are implemented as compile-time constructs (i.e., with template formulations), offers the possibility to use the compiler to check and help eliminate bugs in the code, for example if quantities are incommensurable. In this regard, `your compiler is your friend' [Sutter_2005] by issuing error or warning messages whenever illegal constructs are encountered.
PhysicalQuantities
... (to be written) ...
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 _installdir_/include/Quantities/${VERSION}
with _installdir_
being the installation directory (e.g., given as the --prefix
option during installation of the package) ${VERSION}
being the version number of the Quantities package.
To include specific physical quantity header files, e.g. Time.h
, use
#include "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 "Quantity/Prefix.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"
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
using
directives to your code: using namespace BSUtilities using namespace unit using namespace dimension
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
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);
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_installdir_/lib -lPhysicalQuantities -lQuantity
BSUtilities
library:-L_BSUtilities_installdir_/lib -lBSUtilities
lib/
(and the corresponding path to the BSUtilities library must be present either in ld.so.conf
or in LD_LIBRARY_PATH
.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
Time 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 explicitly delete
the object later when it is no longer needed, in order to avoid memory leaks (see Destruction of Quantity Objects), orA 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;
Time t(5.0); // t = 5.0 s
Time t(5); // t = 5.0 s
Time t(`a'); // t = 97 s
std::string namestring ("name"); std::string symbolstring ("symbol"); Time t(1.0, namestring, symbolstring);
Time t(1.0, "name", "symbol");
Time t(5.0, "min"); // t = 5 min = 300 s, as above
UnitMismatch
exception (Exceptions) is thrown. Thus, Time t(5.0, "m"); // exception at run time
std::string str ("min"); // define string Time t(5.0, str); // t = 5 min = 300 s, as above
MinuteTime t (1.0, Minute ()); // construction, 1 min Time t1 (t); // copy construction, t1 stores 60 s
Time t (1.9); // construction int exp = 2; // exponent for power function SquareTime tsq (pow(t, exp)); // calculate t^2, use dynamic // return object for copy // construction Time t1 (pow (t, exp)); // DimensionMismatch exception SquareMinuteTime tm1 (pow (t, exp)); // calculates t^2, stores in // unit min^2 after // recalculation
SquareTime
and SquareMinuteTime
are quantity types to store a squared time quantity in s^2 or min^2, respectively.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 link 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: ThermodynamicTemperature T (298.0);// construction, 298 K CelsiusTemperature T1 (T); // copy construction, 25 oC
Time 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. Time t = Time (5.); // copy construction by unnamed object // t now contains now 5 s Time t1 (5.); // t1 = 5 s; preferred
t
(see also [Dewhurst_2003]), it should possibly be avoided, and direct construction, as for t1
above should be preferred.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
... conversion by copy construction ...
... conversion by assignment ...
... conversion in special cases ...
... check (copy) construction discussions above !!!!! ...
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:
double
, int
, etc.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
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
Time t (5.9); // construction of variable time, 5.9 s ConstantTime tc = t; // copy initialization
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
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<>
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.
Moreover, as result of these operations a dimensionless generated transient unnamed object is returned, which should be assigned to a full fleged quantity before further use by the user program. It is ok to use the generated transient unnamed object in further arithmetic calculations. For further storage and later reference, however, assign it to a dimensionless quantity, for example PureNumber. Assignment to an object of the storage type is no longer possible in Quantities
after version 1.2.1.
For example, if Time and Frequency have storage type double
Time t (1.0); // constructor, 1 s Frequency omega (10.0); // constructor, 10 Hz PureNumber t_res0; // default construction t_res0 = exp (omega * t); // assignment PureNumber t_res1 = log10 (omega * t); // copy initialization // for multiplication, // see below, // t_result: 1.0 with unit pureNumber::Unity
operation | example function call | comments |
exp | PureNumber t_result = exp (t1); | calculate the exponential of the quantity object value |
log | PureNumber t_result = log (t1); | calculate the natural logarithm of the quantity object value |
log10 | PureNumber t_result = log10 (t1); | calculate the logarithm (base 10) of the quantity object value |
sin | PureNumber t_result = sin (t1); | calculate the sine of the quantity object value it is assumed that the value is in rad |
cos | PureNumber t_result = cos (t1); | calculate the cosine of the quantity object value it is assumed that the value is in rad |
tan | PureNumber t_result = tan (t1); | calculate the tangent of the quantity object value it is assumed that the value is in rad |
sinh | PureNumber t_result = sinh (t1); | calculate the hyperbolic sine of the quantity object value it is assumed that the value is in rad |
cosh | PureNumber t_result = cosh (t1); | calculate the hyperbolic cosine of the quantity object value it is assumed that the value is in rad |
tanh | PureNumber t_result = tanh (t1); | calculate the hyperbolic tangent of the quantity object value it is assumed that the value is in rad |
asin | PureNumber t_result = asin (t1); | calculate the arc sine of the quantity object value it is assumed that the value is in rad |
acos | PureNumber t_result = acos (t1); | calculate the arc cosine of the quantity object value it is assumed that the value is in rad |
atan | PureNumber t_result = atan (t1); | 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. |
exp
, log
and log10
) functions' arguments are always interpreted as being in rad. This is similar to the behavior of the C/C++ math.h functions. PlaneAngleQuantities (see below, Plane Angle Quantity and Units) are a special case, since arguments of objects of a type derived from this base quantity recalculate their value automatically into rad before performing the trigonometric function. operation | example function call | comments |
unary+ | return an object with the value of the object itself (no change in sign) the return quantity type is identical to that of the original object; assignment to this object is not allowed. | |
unary- | return an object with the negative value of the argument (sign change); the return quantity type is identical to that of the original object; assignment to this object is not allowed. | |
prefix increment | increment the value of the argument; the operator returns a reference to the changed original object; assignment to the return from this operator is possible; this operator is only defined for variable quantity objects, but not for constant or unique constant quantity objects. | |
postfix increment | increment the value of the argument; to mimic the behavior of the predefined postfix increment operator for built-in types, the operator returns to a const temporary copy of the unchanged original object; assignment to this object is not allowed; the operator is only defined for variable quantity objects, but not for constant or unique constant quantity objects. | |
prefix decrement | decrrement the value of the argument; the operator returns a reference to the changed original object; assignment to the return from this operator is possible; this operator is only defined for variable quantity objects, but not for constant or unique constant quantity objects. | |
postfix decrement | decrrement the value of the argument; to mimic the behavior of the predefined postfix decrrement operator for built-in types, the operator returns to a const temporary copy of the unchanged original object; assignment to this object is not allowed; the operator is only defined for variable quantity objects, but not for constant or unique constant quantity objects. | |
operator* | multiply from the right; The object to the right of the operator can not only be of the storage type. This is no problem, if the type of this object is such that it can safely be converted or promoted into the storage type of the quantity object. Thus, the above statement should give the desired result, since the int value can readily be converted into the default storage type double , and the result be used for the multiplication. However, if precision is lost during such a conversion, care should be taken. For example, IntTime t(1.0); // construction, 1 s (integer value) IntTime t1 = t * 1.3; // will loose fraction part of double value // 1.3 As with construction of quantity objects (see Construction of Quantity Objects), due to the promotion/conversion properties of C++, possibly non-meaningful constructs can be written, e.g. Time t(1.0); // construction, 1 s (integer value) Time t1 = t * 'a'; // will use encoding for character 'a', // depending on the character set. In any case, the return quantity type is identical to that of the original object, except for the fact that a variable object is returned even if a constant or unique constant object is multiplied. | |
operator* | multiply from the left; Similar comments as regards the storage type as given above for the multiplication from the right apply here. The return quantity type is identical to that of the original object, except for the fact that a variable object is returned even if a constant or unique constant object is multiplied. | |
operator*= | Time t(1.0); // construction, 1 s t *= 5; // t contains 5 s | multiply by an object of storage type from the right and assign to the object itself; the object must be of variable mode; Similar comments as regards the storage type as given above for the multiplication from the right apply here. 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 on the right and assign to the object itself; the object must be of variable mode; Similar comments as regards the storage type as given above for multiplication apply here. the return quantity type is identical to that of the original object; no new object is generated | |
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/= | Time t(1.0); // construction, 1 s t /= 5; // t contains 0.2 s | divide by an object of storage type and assign to the object itself; Similar comments as regards the storage type as given above for multiplication apply here. 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 a variable with identical dimension and unit as the argument; 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 a variable with identical dimension and unit as the argument; | |
floor | calculate the largest integer value not greater than the argument; the return quantity type is a variable with identical dimension and unit as the argument; | |
modf | Time t(1.1); // construction, 1.1 s Time t_int; // default construction, // will be overwritten Time t1 = modf(t, &t_int); // t1 contains 0.1 s // t_int contains 1 s Time t2(30.1); // construction, 30.1 s MinuteTime tm_int; // default construction Time t1 = modf(t, &tm_int); // t1 contains 0.1 s // tm_int contains 0.5 min | decompose the first argument into integer and fraction parts; the return quantity type corresponds to that of the first argument; it is always of mode variable, irrespective of the mode of the first argument; this quantity object contains the fraction part; the second argument must be a pointer to an object of the same quantity type or a quantity derived from the same base quantity as that of the first argument; this quantity object returns the integer part |
frexp | decompose according to the C math.h frexp function; the return quantity type is a variable with identical dimension and unit as the first argument; 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 a variable with identical dimension and unit as the first argument; 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 | Time t(2.); // construction, 2 s BSUtilities::Rational<long(2),long(1)> two; // declare a rational number: 2 SquareTime tsq = pow(t, two); // tsq contains 4 s^2 // Rational<N,D> is a rational number // with numerator N and denominator D // it is defined in namespace BSUtilities | raise the first argument to a power which is given as a rational number; the numerator and denominator of the Rational must be compile time constants; in general, the return quantity type differs from that of the original object, except for Rational<1,1> as the second argument; the result is calculated from the value in the standard unit. note: SquareTime is a quantity which holds squared times. |
pow | Time t(2.); // construction, 2 s BSUtilities::Rational<long(2),long(1)> two; // declare a rational number: 2 SquareTime tsq = t.pow(two); // tsq contains 4 s^2 // Rational<N,D> is a rational number // with numerator N and denominator D // it is defined in namespace BSUtilities | raise the first argument to a power which is given as a rational number, alternative formulation; the numerator and denominator of the Rational must be compile time constants; in general, the return quantity type differs from that of the original object, except for Rational<1,1> as the argument the result is calculated from the value in the standard unit. |
pow | raise the first argument to an integral power; the integer must be a compile time constant and is converted into C++ type by Loki's Int2Type; in general, the return quantity type differs from that of the original object, except for Int2Type<1> as the second argument; the result is calculated from the value in the standard unit. note: SquareTime is a quantity which holds squared times. | |
pow | Time t(2.); // construction, 2 s SquareTime tsq = t.pow(Loki::Int2Type<2>); // tsq contains 4 s^2 | raise the first argument to an integer power; alternative formulation; the integer must be a compile time constant and is converted into C++ type by Loki's Int2Type; in general, the return quantity type differs from that of the original object, except for Int2Type<1> as the argument the result is calculated from the value in the standard unit. |
pow | Time t(2.); // construction, 2 s int i = 2; // run time int exponent SquareTime tsq = pow(t, i); // tsq contains 4 s^2, assignment SquareTime tsq1 (pow(t, i)); // tsq contains 4 s^2, copy construction double d = 2.0; // run time double exponent tsq = pow(t, d); // tsq contains 4 s^2, assignment tsq1 (pow(t, d)); // tsq contains 4 s^2, copy construction tsq = t.pow(d); // alternative formulation Time t1 (pow(t, i)); // results in DimensionMismatch exception | raise the first argument to a power which is given as an integer or as a double; this is a dynamic version of the pow function, and i or d can be variables at run time; float exponents are also possible; for all cases, also the alternative formulation is provided; in general, the return quantity type differs from that of the original object, except for i = 1 as the second argument; since the exact return type is not known at compile time, the result of pow is returned as a dynamic quantity object (type Dynamic<ST>, where ST is the storage type); this can be assigned to a commensurable variable object or such an object can be copy constructed; if the object assigned to or to be constructed is incommensurable, this is detected at run time and a DimensionMismatch exception (Exceptions) is thrown; the returned dynamic quantity object can not be used directly in further calculations, rather it should be converted immediately into a commensurable variable object; this improves safety when working with such values; the result is calculated from the value in the standard unit; however, any combination of allowed units is possible. Thus, Time t (300.); // construction, 300 s MinuteTime tm (5.); // construction, 5 min int exp = 2; // exponent SquareTime tsq (pow (t, exp)); // tsq = 90000 s^2 SquareTime tsq1 (pow (tm, exp)); // tsq1 = 90000 s^2 SquareMinuteTime tsqm (pow (t, exp)); // tsqm = 25 min^2 SquareMinuteTime tsqm1 (pow (tm, exp)); // tsqa1m = 25 min^2 |
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 copied into t1 MinuteTime t2 (sqrt (t) * sqrt (t)); // return value 60 s is copied // into t2 - 1 min
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
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).MinuteTime tm (4.251); // construction, 4.251 min int i; // to receive the integer exponent Time t = frexp (tm, &i); // apply frexp function, result // t = 31.885 s, i = 3 Time t1 (3.2); // construction, 3.2 s MinuteTime tm1 = frexp (t1, &i) // apply frexp function, result // tm1 = 0.01333 min, i = 2
MinuteTime tm (4.251); // construction, 4.251 min int i; // to receive the integer exponent MinuteTime tm1 = frexp (tm, &i) // apply frexp function, result // tm1 = 0.531375 min, i = 3
Among the mathematical operations linking two quantity objects, some require that the two objects are commensurable, i.e. for addition.
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
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
Time t (60.); // construction, 60 s MinuteTime tm (1.); // construction, 1 min t += tm; // result: t is now 120 s
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 only at the right hand side of a += operator, but not on the left hand side, since generated quantities are not design to be assigned to.
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
Time t (5.); // construction, 5 s MinuteTime tmin (0.5); // construction, 30 s PureNumber result = atan2 (t, tmin); // calculate result: // 0.165149, assigned // to PureNumber object
Time t (7.); // construction, 7 s Time t1 (2.); // construction, 2 s Time t2 = fmod (t, t1); // result: 1 s
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
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
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 // ...
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 // ..
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 // ..
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 Quantities
package does not implement any of the schemes described in these references. Thus, if necessary, this has to be included in client code.Serialization [BoostSerialization] can be also regarded as a particular type of input/output. This will be discussed in a separate section.
#include <iostream> Time t (1.0); // construction, 1 s cout << t; // write "1 s" to cout t >> cout; // write "1 s" to cout
#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 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"
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"
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
Time t; // default construction, 1 s t << "xxxx s"; // InputError thrown
Time t; // default construction, 1 s t << "3.5 x"; // InputError thrown
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 (Exceptions) 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. Time 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:.
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
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 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
Time t; // default construction t.name (); // returns the name t.symbol(); // returns the symbol
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.
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"
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"
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
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"
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 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"
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.
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
The following low level 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)
UnitPointerBySymbol<>
UnitIndexBySymbol<>
ListUnitSymbols<>
AllUnits<>
DefaultUnit<>
GenerateVariable<>
SameDimensioned<>
CheckSecondDimension<>
CheckUnit<>
CheckUnits<>
Unit::name ()
Unit::symbol ()
Unit::is_SI ()
Unit::Name ()
Unit::Symbol ()
Unit::Is_SI ()
Unit::version ()
Unit::Version ()
exact ()
ratio ()
dynamic_standardize ()
dynamic_reverse ()
Standardize<>
Reverse<>
Quantity::Name ()
Quantity::Symbol ()
further helper functions in Unit and Dimension classes
new
should later be explicitly deleted: 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.
AssingmentError<true>
should occur in the compilers output, if assignment to a generated quantity 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 may be buried in a large number of output lines, and are often not easily found.
The following exceptions could be thrown:
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 requested substrings.
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 base quantity's type.
DimensionMismatch
is thrown if an operation involving a Dynamic quantity is requested and the dimensional information is incommensurable.
VectorOutOfBounds
(to be written) 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 }
Dynamic q; int exp = 2; // DimensionMismatch expected try {q = pow (q, exp);} // dimensions are incommensurable catch (DimensionMismatch) { // handle exception }
try { // ... // try some code } catch (DimensionMismatch error) { std::cout << error.message () << std:: endl; // print out the text message }
Quantities
is a package that is implemented mostly in the form of C++ template classes and functions. It is thus extandable by instantiating additional instances of these templates. An extension needs to define a new dimension, new units, a new base quantity and new derived quantities. In some cases, more simple modifications of existing quantities is possible.... defaults ...
(to be written)
... needs DerivedQuantity type ...
... needs DerivedQuantityTraits traits template ...
... defaults ...
... define OverwriteName/Symbol bools ...
... if bool set to true -> define the string, if set to false -> no definition of string is necessary ...
... define StorageUnit ...
... decide on the mode ...
... for UniqueConstant: only ONE for a particular derived quantity, unresticted number for a base quantity; if several UniqueConstants are needed based on a base quantity -> use different DQT ...
... can be defined ad hoc without typedef ...
... physical quantities, base quantities, derived quantities ...
... fundamental physical constants (natural constants) ... (to be written)
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.
[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