60.3. Defining a primitive filter

Let's move to stage 2 of the filter program. The user defines the behavior of the filter program by defining primitive filter objects that are constructed by the user and registered to the framework. For the remainder of this section, the term filter will refer to these objects rather than the program.

All filters used in the filter framework must derive from the CFilter base class. A derived class of CFilter will define specific operations on the different types of ring items by providing a new implementation of the specific ring item handler. There are handlers defined for each of the different ring item types:


CRingItem* handleStateChangeItem(CRingStateChangeItem*);
CRingItem* handleScalerItem(CRingScalerItem*);
CRingItem* handlePhysicsEventItem(CPhysicsEventItem*);
CRingItem* handleFragmentItem(CRingFragmentItem*);
CRingItem* handlePhysicsEventCountItem(CRingPhysicsEventCountItem*);
CRingItem* handleTextItem(CRingTextItem*);
CRingItem* handleRingItem(CRingItem*);       
      

Each handler accepts as an argument a ring item read from the source and will output a ring item to send to the sink. The framework will use the type of each ring item it reads from the source to call the appropriate handler method. Also, it is worth noting that handlers receiving a specific ring item type can actually output any ring item type. [1] It is not clear why the user would want to do this, but it is possible to, for example, mutate scaler items into physics event items by means of a filter. It is also possible to prevent an item from being sent to the sink by return 0 or nullptr from a handler.

Implementing code for all of the handlers listed above is not necessary because each handler has a default implementation in the base class. These base implementations do nothing more than return the argument. An example code snippet is included to illustrate this:


CRingItem* CFilter::handleTextItem(CRingTextItem* item) {

	return static_cast<CRingItem*>(item);

}
      

The CFilter base class provides transparent handlers, i.e. it outputs the exact data it receives as input. The base class handlers are defined as virtual methods so that if the derived class implements a method with the same signature as the base class, its implementation will be used instead of the base implementation. This allows the user to define only the handlers relevant to their problem area.

The CTemplateFilter.cpp source file contains a basic implementation of a filter. It solves the unrealistic problem of needing to produce a mirror image of event data. It copies event data in reverse order to the end of the body. This hopefully demonstrates the flexibility that exists in the framework. Notice that the TemplateFilter only implements one handler.

In addition to handlers, user-defined filters can implement hooks that get called prior to and after processing all ring items. The two methods have the following signatures:

 class CFilter {

  void initialize();
  void finalize();
}

The initialize() method is executed prior to the first ring item is processed and the finalize() method is executed after the last ring item is processed.

Notes

[1]

There is a limitation to this behavior though. If you have registered more than one filter to the framework, and the first one mutates the type of the ring item, the handler of the second filter will operate on the ring item as though it were the original type.