The API provides methods that allow you to dynamically change the contents of the event processing pipeline. You can add event processors at any point in the pipeline, remove them from any point in the pipeline and iterate the pipeline.
The example we will show in this section will be one that can swap out, and back, entire event processing pipelines. Our specific use case will be to save the contents of an event processing pipeline so that it can be restored later, clear the pipeline and insert the event processor that can unpack filter files.
In libTcl++ and adding commands to SpecTcl, we will combine the class we're writing here with a Tcl command added to SpecTcl to allow us to switch dynamically between analyzing raw event files and analyzing filter files.
The class we're going to write has the following interface specification:
Example 4-10. Class to switch event analysis pipelines
#ifndef EVPSWITCHER_H #define EVPSWITCHER_H #include <vector> #include <string> class CEventProcessor; class CFilterEventProcessor; class EVPSwitcher { private: std::vector<std::pair<std::string, CEventProcessor*> > m_savedPipeline; CEventProcessor& m_FilterProcessor; public: EVPSwitcher(); ~EVPSwitcher(); void save(); void restore(); void useFilterProcessor(); }; #endif
The concept is fairly simple. The save
method will save the current event processing pipeline processor
names and object pointers into the m_savedPipeline member.
Similarly restor
, restore will replace
the current event processing pipeline with the processors
saved in m_savedPipeline
.
useFilterProcessor
will save the pipeline
and replace it with one that contains the filter event processor
in m_FilterProcessor
. This attribute will
be created by the constructor and destroyed by the destructor.
Let's have a look at the constructor and destructor as they're closely linked:
Example 4-11. EVPSwitcher constructor and destrutor
#include "EVPSwitcher.h" #include <EventProcessor.h> #include <FilterEventProcessor.h> #include <SpecTcl.h> #include <TCLAnalyzer.h> EVPSwitcher::EVPSwitcher() : m_FilterProcessor(*(new CFilterEventProcessor)) {} EVPSwitcher::~EVPSwitcher() { delete &m_FilterProcessor; }
Straightforward code. The constructor creates a filter event
processor, saving a reference to it in m_FilterProcessor
.
The destructor prevents this from being a memory leak if we are
destroyed.
The save
method will:
Clear the m_savedPipeline
vector.
We only need to clear the vector. We don't destroy any objects.
Walk the event processing pipeline using the
SpecTcl::ProcessingPipelineBegin
and SpecTcl::ProcessingPeiplineEnd
methods to get iterators that "point" to successive
event processign pipeline elements.
Each event processing pipeline element is a pair containing the name of the pipeline element and a pointer to the event processor in that position. Ths is exactly what we need.
The std::vector::insert
method makes
this all pretty trivial:
Example 4-12. EVPSwitcher::save
implementation
void EVPSwitcher::save() { m_savedPipeline.clear(); SpecTcl& api(*SpecTcl::getInstance()); m_savedPipeline.insert(m_savedPipeline.end(), api.ProcessingPipelineBegin(), api.ProcessingPipelineEnd()); }
restore
is a matter of:
Removing event processing pipeline elements until the event processing pipeline is empty.
Inserting each event processor into the now emptied event processing pipeline.
Since emptying the event processing pipeline is also needed
for useFilterProcessotr
, we'll
introduce a utility method; clearPipeline
.
Here's the implementatiuon of that utility and
restore
.
Example 4-13. EVPSwitcher::restore
implementation
void EVPSwitcher::clearPipeline() { SpecTcl& api(*SpecTcl::getInstance()); while(api.ProcessingPipelineBegin() != api.ProcessingPipelineEnd()) { api.RemoveEventProcessor(api.ProcessingPipelineBegin()); } } void EVPSwitcher::restore() { clearPipeline(); SpecTcl& api(*SpecTcl::getInstance()); for (int i = 0; i < m_savedPipeline.size(); i++) { api.AddEventProcessor(*m_savedPipeline[i].second, m_savedPipeline[i].first.c_str()); } }
Much of this should be straightforward. The only bits that are
interesting is that the call to
SpecTcl::AddEventProcessor
requires
a reference to the event processor and a const char*
name.
Finally to set up SpecTcl to read data from filtered data files, we need to:
Clear the event processing pipeline
Add our filter event processor to the pipeline.
In all of this, the assumption is that prior to this,
if someone wants the event processing pipeline restored,
they will have first invoked
save
to do that.
Example 4-14. EVPSwitcher::useFilterProcessor
implementation
void EVPSwitcher::useFilterProcessor() { clearPipeline(); SpecTcl::getInstance()->AddEventProcessor(m_FilterProcessor, "Filter-decoder"); }
I think again, no explanation is needed.