Next: Compiling and Installing
Up: Overflow User's Manual
Previous: Software Architecture
  Contents
Subsections
Overflow is designed to be extendible in many areas, so that it is
possible to create new: node types, operators and data types.
Most of the new nodes will derive from either the Node abstract class
or the BufferedNode abstract class. You should use public inheritance
when deriving your new class. In all cases, you will need to define
a constructor for your new node class. The parameters for this constructors
are: (string nodeName, const ParameterSet ¶ms), which
are used to initialize the base class, e.g.
-
- class MyNode : public BufferedNode {
public:
MyNode(nodeName, params) : BufferedNode(nodeName, params)
...
};
Also, if you derive from BufferedNode, you need to define the virtual
void calculate(int output_id, int count, Buffer &out) method. The
arguments are the ID of the input requested (output_id),
the iteration ID (count) and the output buffer for the requested
output (out). The calculate method is expected to
assign an object to out[count].
If you derive directly from the Node class, you will need to override
the ObjectRef getOutput(int output_id, int count) method.
The meaning of output_id and count is the same
as for the BufferedNode equivalent, and the result should be returned
as an ObjectRef.
Here are some other methods you might want to define too:
- void initialize(): As the name implies, it is meant to perform
some initialization that cannot be done within the constructor. This
method is called only once, starting the processing, but after all
the request() have been made. In most (all) cases initialize()
should start by calling the base class implementation (e.g. BufferedNode::initialize()).
- void reset(): This method should return the node to the same
state it was after initialize() was first called. In most
(all) cases reset() should start by calling the base class implementation
(e.g. BufferedNode::reset()).
In some rare cases, you will want to define the following method:
- void request(int outputID, const ParameterSet &req): This
method is meant to pass on special requests to input nodes.
For now, this is mainly used by the BufferedNode class to compute
the size needed for the output buffers. Remember that if you override
this method, you must make sure that it propagates the request
to all its input nodes. Otherwise, the nodes that won't be
reached will have incorrect buffer size.
At last for a new node to be visible in vflow, a special header
must be present. An example of this is:
-
- class MyNode;
DECLARE_NODE( MyNode)
/*Node
*
* @name MyNode
* @category MyCategory:MySubCategory
* @description Some description of what MyNode does
*
* @input_name SOME_INPUT_NAME
* @input_type this_input_type
* @input_description Description of this input
*
* @input_name SOME_OTHER_INPUT
* @input_type that_input_type
* @input_description Description of that output
*
* @output_name SOME_OUTPUT
* @output_type this_output_type
* @output_description Description of the output
*
* @parameter_name SOME_PARAMETER
* @parameter_type this_parameter_type
* @parameter_description The description of the parameter
* END*/
Although this header is only a C++ comment, it is parsed by a PERL
script to produce an XML description of each toolbox. The DECLARE_NODE(MyNode)
macro is used to register the node in a dictionary when the toolbox
is dynamically loaded.
Most nodes must include BufferedNode.h. Also, since this node
deals with vectors, we need Vector.h
-
- #include "BufferedNode.h"
#include "Vector.h"
forward declaration of class VAdd for use with the DECLARE_NODE
macro
-
- class VAdd;
Declaration of the node. This definition is transformed into
XML data for the GUI, as well as documentation for the node
-
- DECLARE_NODE(VAdd)
/*Node
*
* @name VAdd
* @category DSP:Base
* @description Adds two vectors of same length
*
* @input_name INPUT1
* @input_type Vector<float>
* @input_description First vector
*
* @input_name INPUT2
* @input_type Vector<float>
* @input_description Second vector
*
* @output_name OUTPUT
* @output_type Vector<float>
* @output_description Result vector
*
END*/
Class definition/implementation. Note that because we won't
need to derive from this class, we don't need a header file (.h) and
we can put everything in the .cc. Our node, like most other nodes,
derives from BufferedNode.
-
- class VAdd : public BufferedNode {
int input1ID;
int input2ID;
int outputID;
public:
VAdd(string nodeName, ParameterSet params)
: BufferedNode(nodeName, params)
{
In the constructor, we create both the inputs and outputs.
-
- input1ID = addInput("INPUT1");
input2ID = addInput("INPUT2");
outputID = addOutput("OUTPUT");
}
This is the main method for the node, it is called from the
BufferedNode class each time a result needs to be calculated.
-
- void calculate(int output_id, int count, Buffer &out)
{
Get input data from previous node(s).
-
- ObjectRef input1Value = getInput(input1ID, count);
ObjectRef input2Value = getInput(input2ID, count);
We cast the generic objects (received through ObjectRefs)
into a reference to a Vector<float>. If the cast fails, an exception
will automatically be thrown.
-
-
const Vector<float> &in1 = object_cast<Vector<float> > (input1Value);
const Vector<float> &in2 = object_cast<Vector<float> > (input2Value);
Check that the size of the two vectors match. Otherwise, throw
an exception. Here __FILE__ and __LINE__ are pre-processor
macros that will print the file and line where this exception was
thrown.
-
- if (in1.size() != in2.size())
throw new NodeException(this,
"Input vectors must be of same length",
__FILE__, __LINE__);
int inputLength = in1.size();
Allocate a new Vector<float> from the pool of free vectors
(that's why we don't use new).
-
- Vector<float> = &output =
*Vector<float>::alloc(inputLength);
Put the new Vector<float> in the return buffer.
-
- out[count] = &output;
Compute the result of the sum.
-
- for (int i=0;i<inputLength;i++)
output[i]=in1[i]+in2[i];
}
};
It is possible to define binary operators that can act on different
kinds of input. One example is the "add" operator,
which can be used to add two ints, two floats, two vectors, or an
int and a float, ... See data-flow/include/operators.h
It is possible to define new types in Overflow. In order to be used
in new nodes, new types must derive from the Object base class. That
the only absolute requirement. However, if you want the new type to
integrate more closely with Overflow, there are several things you
can do:
- Implement the void printOn(ostream &out) const method. This
method writes the object to the out stream.
- Implement the void readFrom (istream &in).
- Add the macro DECLARE_TYPE(MyType) to the
C++ file where the object is implemented.
There is a certain format which all Object must respect. The object
should start with "<MyType" and end with ">"
(without the quotes). Usually, every field will be inside < and >
signs.
Next: Compiling and Installing
Up: Overflow User's Manual
Previous: Software Architecture
  Contents
Jean-Marc Valin
2002-06-17