Processing infrastructure

All algorithms in CLAM are encapsulated in a class derived from the abstract Processing base class. Thus it can be said that every concrete derived class is a ``Processing class'' and each of its instances is a ``Processing object''. The main goal of this abstract class is to minimize the developer's effort when introducing a new algorithm while enforcing good programming practices that produces a code that results both efficient and understandable. For this reason, the class provides some services and enforces some particular implementations through its interface.

The Processing base class forces all derived classes to implement a port mechanism. Processing objects communicate with other Processing objects through the use of ports. Input ports introduce data into a Processing unit and the result of the process is written into the output port.

All concrete Processing classes also need to declare a related configuration class. This class is derived from the base ProcessingConfig class and its name is recommended to be the same as the one of its associated Processing class adding the ``Config'' suffix. In some cases several processing classes may share the same configuration class. The FFT is a clear example of this. For example, several FFT classes exist for different algorithm implementations in the CLAM repository, but they all derive from a common FFT_base class, and they share a common configuration class: FFTConfig.

Figure 3.4: FFT's and FFTConfig

The configuration will be used for initializing the Processing object before its execution. For enforcing the use of this configuration mechanism, the base Processing class declares a ConcreteConfigure() pure virtual method that must be implemented in any concrete derived class. In this method all initialization operations related to the configuration stage must be implemented. As a matter of fact, the configuration is accomplished by calling the Configure() operation in the base class. This operation implements the Template Method design pattern [Gamma et al., 1995]: general operations like internal state modification is modified in the base class while concrete and particular configuration issues are delegated to each derived class. It is also very common for Processing classes to keep an internal copy of the configuration object to be able to respond when queried about their current configuration.

Two operations are used in order to get a Processing into execution state and to force it to leave this state: Start() and Stop(). These operations also implement the Template Method design pattern by implementing the default behavior (i.e. a simple change of state) in the base class and leaving specific issues to the ConcreteStart() and ConcreteStop() method in derived classes. These methods, though, are not abstract and its implementation is therefore not mandatory.

Processing classes must provide two constructors: a default constructor and a constructor with the configuration class as its argument type. In most cases the former will call the Configure operation with a default constructed configuration while the second will call it passing the received configuration object. It is also usual to include some explicit calls to member constructors for initializing members like Controls or Ports that do not have default constructors. Apart from that, most of the initialization functionality will be left to the ConcreteConfigure() method.

The main execution methods in a Processing class are the Do methods. They are the ones which actually perform the processing action.

There are two different kinds of Do methods:

  1. A Do(void) method, with no arguments. This is the standard way of using Processing objects connected to a Network (see 3.2.2).
  2. Do(...) methods taking data objects as arguments. They will have some input data arguments first, and then some output data arguments. A typical processing class will need a single Do method of this kind.
Both kinds of Do() methods operate in the same way: they read a certain number of data objects from each of the inputs, and write a certain number of data objects to each of the outputs. The difference is that the non-network Do() method takes this data objects as arguments (and thus does not use ports), while the network Do() method has no arguments, an accesses the Data through the Ports objects. It is very usual that this latter version of the Do operation calls the explicit argument version after having extracted the data from the corresponding ports.

Figure 3.5: Typical CLAM execution sequence