Automatic type conversion
The problem of proliferation of formats is not limited to files. It
occurs in the structures used to represent the data used within
programs as well. If there are N different representations for some
data within a program, and M different file formats, there could be
a requirement for up to N*M load routines.
Fortunately the concept of a `Process' for transforming data from the
previous section provides a solution. As has been done for file
formats it is possible to keep a database of known transformations
between different data representations. Using this database we can
identify a transformation or set of transformations that converts
between the representation used by a file format and that used by the
program.
The set of transformations known to the program can be throught of as a
graph, each node corresponding to a data representation, and the edges
as transformations between them. The problem of finding the quickest
way of converting one type to another becomes one of searching for
the shortest path through the graph. This is a well-known and studied
problem, and is easily solved. Figure 17 shows a
simple type conversion graph. The numbers shown next to the edges are
the defined costs of conversions which will be discussed next. It is
possible to convert between any two nodes in this graph even though
not all conversions are defined; it is this consideration which
justifies the added complexity of using such graphs, over a simple
conversion list.
The nodes of the graph in Figure 17, are represented
by the class `DPTypeInfoC', and the edges by the class `DPProcInfoC'.
Both these classes contains virtual methods which allow the type conversion
mechanism both to handle data, and construct conversion pipes without
any direct reference to types themselves.
Figure 17:
A simple type conversion graph.
 |
Sometimes information is lost in a conversion, e.g. turning a real
number into an integer. This means that not all paths through the
graph are equally desirable. Since the IO routines can not know what
aspects of the information being handled are important we cannot
guarantee the best conversion path is chosen. It is possible to
define a heuristic, which chooses the path which 'loses' the least
information. For this a value is associated with each conversion
which estimates the relative information lost in the conversion. The
value is provided by the programmer, and should be the ratio of bits
used in the representation of the output divided by the number used to
represent the input data. The graph search then becomes a search for
the least cost, leading to a problem with known solutions from
computer science. The heuristic described above is a little ad-hoc,
but in practice has provided satisfactory behaviour.
With the use of templating the addition of new conversion can be made
very easy. As with processes described earlier only a single function
need be defined. The following example defines the conversion from
an interger to a floating point number, (defined as RealT in AMMA),
which loses no information and hence has a cost of one.
RealT DPConvIntT2RealT ( const IntT &val )
{ return ( RealT) val ; }
DP_REGISTER_CONVERTION ( DPConvIntT2RealT,1 ) ;
The type conversion mechanism is available should the user wish to use
it. The interface consists of 3 functions. The first, 'DPCanConvert'
tests if a conversion is possible, and takes as argument references
the C++ rtti type_info structure. A Boolean is returned indicating
whether a conversion is possible. The second function,
`DPDoConvertion' takes as arguments two abstract handles, RCAbstractC,
which can wrap any other C++ type rather like a void * in 'C'. This
will convert the handle from its original type to the one required, if
possible. If the conversion fails an invalid handle is returned.
Finally a templated function, DPTypeConvert is provided which wraps
the `DPDoConvertion' operation so the user can use the type conversion
without any special constructions. This final mechanism is provided
mainly for testing the type conversion mechanism, as if both types are
fully defined, conversion can be done through conventional C++
means. The following example uses the interface to convert an integer
to a double, the hard way.
int i = 10;
double j;
if(DPTypeConvert(i,j))
{ cout << "Conversion succeeded"; }
Normal classes:
Normal functions: