| 5 Subclassing the data types |
The DAP defines a data type hierarchy as the core of its data model. This
collection of data types includes scalar, vector and constructor types. Most
of the types are available in all modern programming languages with the
exceptions being Url, Sequence and Grid. In the DAP
library, the class BaseType is the root of the data type tree.
The DAP supports the common scalar data types such as Byte, 16- and 32-bit
signed and unsigned integers, and 32- and 64-bit floating point numbers. The
DAP also supports Strings and Urls as basic scalar types. The DAP includes
Arrays of unlimited size and dimensionality. The DAP also supports three
type-constructors: Structure, Sequence and Grid. A
Structure on the DAP mimics a struct in C. A Sequence is a
table-like data structure inherited from the JGOFS data system. It can be
used to hold information that might be stored in relational databases or
tables, either flat or hierarchical. The JGOFS, FreeForm and HDF servers all
use the Sequence data type. Lastly, the Grid data type is used to
bind an array to a group of map vectors, single dimension arrays that
provide non-integral values for the indexes of the array. The most typical
use of a Grid is to provide latitude and longitude registration for some
georeferenced array data (e.g., a projected satellite image). The DAP does
not have a pointer data type, but in some cases the Url data type can
be used as a pointer to variables between files. More information about the
DAP's data type
hierarchy
is given in the Programmer's Guide.
When you build a DAP client, you must create a collection of data type subclasses. That is each of the leaf classes in the preceding class diagram must be subclassed by your client. This is pretty easy since a good bit of the work is rote.
First we'll illustrate the parts that are mechanical. Here's an example from the C++ Matlab client. The class is the Byte class. In the case of the matlab client, this class doesn't do anything beyond the bare minimum, so it's a good starting point:
Byte *
NewByte(const string &n)
{
return new ClientByte(n);
}
BaseType *
ClientByte::ptr_duplicate()
{
return new ClientByte(*this);
}
bool
ClientByte::read(const string &)
{
throw InternalErr(__FILE__, __LINE__, "Called unimplemented read method");
}
To create a child of any of the data type leaf classes, you must define three
methods and one function. Let's talk about the function first. The function
NewByte is what Meyers[2] calls a virtual
constructor. It's similar to a low-budget factory class ("low-budget"
because it's not a class). This function is used at various places in the DAP
library when it need to create instances of Byte without knowing in
advance the dynamic type of the object that actually will be created. If all
this sounds a little weird, just remember that your Byte, Int16,
..., Grid classes -- whatever they may be called -- must all contain
an implementation of this function and each should all return a pointer to an
instance of the appropriate child class. These functions will be used by the
library to create instance of the classes you have defined when writing your
server. In this case of the example Matlab server, it's an instance of the
MATByte class. If you look in the files for the Matlab server, you'll
see that the function NewGrid returns a pointer to a new MATGrid,
and so on.
Second, a constructor must be implemented and should take the name of the variable as its sole argument.
Third, your child classes should also define the ptr_duplicate()
method. This method returns a pointer to a new instance of an object in the
same class. Occasionally, in the DAP library, objects are declared with
pointers specified as BaseType *. If the new operator was used to
copy such an object, the copied object would be an instance of BaseType (the
static type of the object) not the type of the thing referenced (the dynamic
type)4. By using the ptr_duplicate() method the DAP
library is sure that when it copies an object, it's getting an instance of
the subclass defined by your server.
Unlike the case where you are subclassing the DAP variable classes to build a
server, there's no need to implement read() when building a client. The
classes contain a default implementation od read() that throws
InternalErr if it is ever called (which no simple client should ever
do.5
| 5 Subclassing the data types |