3.3. Creating a Single-Value Field

This section shows how to create a single-value field class, SFDouble, that contains a double-precision real number.

The header file for this class uses the SO_SFIELD_HEADER() macro to declare all required member variables and functions for a single-value field. This macro declares the methods and variables for field classes that are derived from SoSField SoSField SoSField . The first argument to the macro is the name of the new field. The second argument is the type of the value stored in the field, and the third is the type that can be passed to or returned from a function. For double, a primitive type, these two arguments are identical. For more

complicated field types, this last type is typically a reference to the field value; for example,

const SbVec3f &

for a field holding an SbVec3f SbVec3f .

If your new field class is derived from another single-value field (rather than from an abstract class), use the SO_SFIELD_DERIVED_HEADER() macro.

Example 3-1 shows the header file for the SFDouble class.

Example 3.1.  SFDouble.h

#include <Inventor/fields/SoSubField.h>

class SFDouble : public SoSField {

   // Declares all required member variables and functions for a
   // single-value field
   SO_SFIELD_HEADER(SFDouble, double, double);

   // Initializes field class, setting up runtime type info
   static void    initClass();

The source file for the SFDouble field class uses the SO_SFIELD_SOURCE() macro to define all required member variables and functions for the single-value field. It uses the SO_SFIELD_INIT() macro to initialize the field class and set up runtime type information.

SO_SFIELD_SOURCE() defines all methods that are declared in SO_SFIELD_HEADER(). SO_SFIELD_DERIVED_SOURCE() defines all methods that are declared in SO_SFIELD_DERIVED_HEADER(). (Even if you did not use the standard macros in your class header because you wanted to implement methods differently, you may still be able to use these macros in your source file. See SoSubField.h for details.)

Two methods for reading and writing the new field are also included. The SoInput SoInput SoInput class defines several read() methods that read a primitive type from the current input. One of these methods reads a number of type double, so we can use that method here to read into the value member variable defined in SO_SFIELD_HEADER(). The read() methods return FALSE on error, which is just what we want. Similarly, the SoOutput SoOutput SoOutput class has several write() methods. The new class uses the one that writes out a double.

Example 3-2 shows the source file for the SFDouble class.

Example 3.2.  SFDouble.c++

#include "SFDouble.h"

// Defines all required member variables and functions for a
// single-value field
SO_SFIELD_SOURCE(SFDouble, double, double);

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

   // This macro takes the name of the class and the name of the
   // parent class

// This reads the value of a double-precision field from a
// file. It returns FALSE if the value could not be read
// successfully.

SFDouble::readValue(SoInput *in)
   // Read a double from the input
   return in->read(value);

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

SFDouble::writeValue(SoOutput *out) const
   // Write a double

For more complex field-value types, you must be careful when writing values; you have to see if the output format is binary before writing spaces or other ASCII formatting. For example, the method to write the value of an SoSFVec3f SoSFVec3f SoSFVec3f field looks like this:

SoSFVec3f::writeValue(SoOutput *out) const
   // Write first component of vector

   // If not writing binary format, output a space between
   // values
   if (! out->isBinary())
      out->write(' ');

   // Repeat for other components of vector
   if (! out->isBinary())
      out->write(' ');