3.4. Creating a Multiple-Value Field

This section shows how to create a multiple-value field class, MFDouble, that contains any number of double-precision real numbers.

Example 3-3 shows the header file for the MFDouble class. It uses the SO_MFIELD_HEADER() macro, which is typically the only macro you need to use in an SoMField( C++ | Java | .NET ) subclass header, unless you wish to change value handling or construction/destruction.

Use the SO_MFIELD_DERIVED_HEADER() macro to declare the methods and variables for fields that are derived from other multiple-value fields (rather than from an abstract class).

Example 3.3.  MFDouble.h


C++
#include <Inventor/fields/SoSubField.h>

class MFDouble : public SoMField {

   // This macro is just like the one for single-value fields.
   SO_MFIELD_HEADER(MFDouble, double, double);

 public:
   static void    initClass();
   static void    exitClass();
   
 private:
   // This returns the number of ASCII values to write per
   // output line. It can be used to produce more compact
   // output files for fields containing small value types.
   virtual int    getNumValuesPerLine() const;
};

The SO_MFIELD_SOURCE() macro defines all methods that are declared in SO_MFIELD_HEADER(). This macro includes several other macros that you may find useful even if you don't want to use all of them.

SO_MFIELD_SOURCE_MALLOC(), used in Example 3.4, “ MFDouble.cxx, is an alternate version of SO_MFIELD_SOURCE(). The difference is that this macro implements value storage management using malloc(), realloc(), and free() , while SO_MFIELD_SOURCE() uses the new and delete operators. While SO_MFIELD_SOURCE_MALLOC() produces faster and more efficient code than the other source macro, it should be used only for field classes whose values have no constructors or destructors (since they would never be called). For example, the SoMFShort( C++ | Java | .NET ) class uses this macro, since values of type short are not C++ instances, and therefore do not have any constructors or destructors. The MFDouble field contains a basic type (a double) that does not need a constructor, so we are free to use this alternate (and more efficient) macro.

SO_MFIELD_DERIVED_SOURCE() defines all methods that are declared in SO_MFIELD_DERIVED_HEADER(). Use these macros if you are deriving a field from another field.

Example 3-4 shows the source file for the MFDouble class.

Example 3.4.  MFDouble.cxx


C++
#include "MFDouble.h"

// Defines all required member variables and functions for a
// multiple-value field. We use the version that allocates field
// value storage with malloc(), since there is no constructor to
// call for our values.
SO_MFIELD_SOURCE_MALLOC(MFDouble, double, double);

// Initializes the class, setting up runtime type info.

void
MFDouble::initClass()
{
   // This macro takes the name of the class and the name of the
   // parent class
   SO_MFIELD_INIT_CLASS(MFDouble, SoMField);
}

void
MFDouble::exitClass()
{
   SO_MFIELD_EXIT_CLASS(MFDouble);
}

// This reads one value of the double-precision field from a
// file. It is passed the index of the value to read; we can
// assume that the field already contains enough room to hold
// this value. It returns FALSE if the value could not be read
// successfully.

SbBool
MFDouble::read1Value(SoInput *in, int index)
{
   // Read a double from the input
   return in->read(values[index]);
}

// This writes one value of a double-precision field to a
// file.

void
MFDouble::write1Value(SoOutput *out, int index) const
{
   // Write a double
   out->write(values[index]);
}

// Returns number of ASCII values to write out per line.

int
MFDouble::getNumValuesPerLine() const
{
   // We can probably fit 4 doubles per line pretty easily.
   return 4;
}