Essential Math Module User’s Guide : Chapter 2 Templatized Collection Classes : Nonstandard Numeric Types
Nonstandard Numeric Types
An important advantage of using templates for collection classes is flexibility. Templates let you create vectors, matrices, and arrays of arbitrary built-in types, and even types you define yourself. For example, you can create a matrix RWGenMat<long double> for doing high-precision floating point calculations, or an array RWMathArray<unsigned short int> for small integers. This flexibility gives you greater control of both accuracy and storage demands for a given application.
Unfortunately, not all compilers conform to the proposed template standards. For this reason, parts of the library are able to support only the six defined types available in earlier versions of the Essential Math Module: DComplex, double, float, int, UChar, and SChar. You can access the full functionality of the library with other numeric types by making a minor change in a library header file, and adding some global functions for the additional numeric types. We will show you how to do this in “Adding New Numeric Types” but let us first discuss the requirements for defining a new numeric type.
Defining a New Numeric Type
If you create an entirely new numeric type not available from your compiler, your numeric class must meet minimum requirements to have full functionality in the Essential Math Module. We include an example numeric class, Rational, located in file rational.h in your Rogue Wave examples directory. You can examine this file and use it as a framework for building your own numeric type.
To meet the minimum requirements for full functionality in the Essential Math Module, a numeric type Num must:
Be constructible either from 0 or 1, or with no arguments; for example, Num x, Num y(0), and Num z(1) are all legal statements.
Define all arithmetic operators; that is, x+y, x-y, x*y, x/y, and -x.
Define all arithmetic with assignment operators; for example, x+=y, x-=y, x*=y, x/=y.
Define all equality and nonequality boolean operators; for example, x==y, x!=y.
In addition, there may be other requirements for a numeric type, depending on your application. These requirements apply only when a type is used in a particular part of the library. For example, the numeric type Num may also be required to:
Define global functions. Any templatized global function used with type Num must be defined as a function that takes and returns a type Num. In our example, Rational, we define sin(Rational), which returns a Rational that is of course an approximation. Defining this function enables the use of the library function sin(const RWMathVec<Rational>&). The Rational class also has an example of a nontemplatized function definition linfNorm(const RWMathVec<Rational>&) that must be defined explicitly in order to use it.
Define file/stream shift operators. The choices for I/O include istream, ostream, vistream, vostream, and RWFile. To use any of these on a collection of type Num, the corresponding shift operator must be defined for type Num. Examples of all six operators are given in the Rational class.
Define an rw_numeric_traits class. You may also need to add an rw_numeric_traits class specialization for your numeric type, so that calculations for this class are handled properly. A default class is provided, but a specialized class may produce better results. You can include the rw_numeric_traits specialization at the end of the numeric type header file, as in the rational.h file. This is the preferred place to define this specialization, but you can assign a different place as explained in the next section.
Adding New Numeric Types
Types other than DComplex, double, float, int, UChar, and SChar are not fully implemented in the Essential Math Module. As mentioned in “Nonstandard Numeric Types” you must do two things to achieve their full functionality:
Make a minor change in the library header file
Add some global functions
Note that most operations work without these additional instructions. However, some portions of the library are unavailable, resulting in compiler errors if you try to access them. Should these problems occur, follow the instructions below. They are only necessary for the portions of the library that you need.
Modifying the Numeric Trait Information
As noted in the previous section, you can include a specialization of the template class rw_numeric_traits in a header file containing the declaration of the new type. For built-in types, you have two choices:
Put the specialization in its own header file, similar to a user-defined type, and include it wherever it is used in your code.
Edit the file rw/math/numtrait.h to add your own specialization.
Details about the typedefs in rw_numeric_traits are described in the SourcePro C++ API Reference Guide.
Adding Global and Specialized Functions
Nontemplatized Global Functions
Most global functions are templatized and should work with both built-in or user-defined types. Due to limitations in the way some compilers implement templates, however, some global functions cannot be templatized, or must be specialized for other reasons.
Primarily this occurs in functions whose return type is different than the input type; for example, abs(RWMathVec<DComplex>) returns an RWMathVec<double>. Also, a few template functions must be specialized for DComplex. As compilers come closer to implementing the Standard C++ Library, these functions will become template functions with their return type defined by rw_numeric_traits.
For functions which are not templatized, you don't need to add all global functions for your numeric type, but only those you want to use. Functions which have been specialized, or are not templatized, are in the Global Functions.
Templatized Global Functions
Some fully templatized functions which operate on the collection classes on a per-element basis, as the trigonometric functions do, assume that the corresponding function is defined for your numeric type. When you use templatized global functions which assume corresponding defined functions, you must be sure to provide them. For example, to compute sin(RWMathVec< Quaternion<double> >), the function sin(Quaternion<double>) must be defined.