In this section, we'll build a simple filter formatter. We're not going to build an unpacker for it because that raises a few issues beyond the scope of this part of the document. We're just going to show:
How to write a filter output stage for a simple CSV form of filter file format.
How to write an output stage creator for the new filter format we're creating
How and where to register the creator with the factory so that the filter command knows about it.
Let's look at the creator. Here's a working definition for our class:
Example 8-4. Definition of CSVFilterOutputStage
#ifndef CSVFILTEROUTPUTSTAGE_H
#define CSVFILTEROUTPUTSTAGE_H
#include <CFilterOutputStage.h>
#include <fstream>
class CSVFilterOutputStage : public CFilterOutputStage
{
private:
std::ofstream m_file;
std::vector <UInt_t> m_params;
public:
virtual void open(std::string filename) ;
virtual void close() ;
virtual void DescribeEvent(std::vector<std::string> parameterNames,
std::vector<UInt_t> parameterIds);
virtual void operator()(CEvent& event);
virtual std::string type() const;
};
#endif
Note that since we're doing a text, comma separated field
file, it's completely appropriate to store the file as an
std::ostream object.
Implementations of open and
close are trivial:
Example 8-5.
Implementation of open and close for CSVFilterOutputStage
void
CSVFilterOutputStage::open(std::string filename)
{
m_file.open(filename);
}
void
CSVFilterOutputStage::close()
{
m_file.close();
}
DescribeEvent is not that harder:
Write a header to the output file.
Save the parameter ids for later use.
Example 8-6. Implementing CSVFilterOutputStage::DescribeEvent
void
CSVFilterOutputStage:: DescribeEvent(std::vector<std::string> parameterNames,
std::vector<UInt_t> parameterIds)
{
for (int i = 0; i < parameterNames.size(); i++) {
char delim = ','; // Delimeter for all but end.
if ((i+1) == parameterNames.size()) {
delim = '\n'; // Last one has a newline following it.
}
m_file << parameterNames[i] << delim;
}
m_params = parameterIds;
}
The only interesting thing about this is a bit of logic we saw in our filter output example and that's how the character that follows the data is chosen to be a , if the item being written is not last or \n if it is.
We'll see that logic again in the operator()
implementation. There, we'll also see a check on whether an event
has defined a parameter or not. If not, then an empty field is written:
Example 8-7. Implementing CSVFilterOutputStage::operator()
void
CSVFilterOutputStage:: operator()(CEvent& event)
{
for (int i = 0; i < m_params.size(); i++) {
int idx = m_params[i];
char delim = ',';
if ((i+1) == m_params.size()) {
delim = '\n';
}
if (event[idx].isValid()) {
m_file << event[idx];
}
m_file << delim;
}
}
This leavses only the type method and
that too is trivial:
Example 8-8. Implmenting CSVFilterOutputStage::type
std::string
CSVFilterOutputStage::type() const
{
return "csv";
}
Our next order of business is to implement a creator for this output stage. The definition of this creator is:
Example 8-9. Definition of CSVFilterOutputStageCreator
#ifndef CSVFILTEROUTPUTSTAGECREATOR_H
#define CSVFILTEROUTPUTSTAGECREATOR_H
#include <CFilterOutputStageCreator.h>
class CSVFilterOutputStageCreator : public CFilterOutputStageCreator
{
public:
virtual CFilterOutputStage* operator()(std::string type);
virtual std::string document() const;
virtual CFilterOutputStageCreator* clone();
};
#endif
Implementations of all of these are quite trivial. We're going to
recognize the type csv and produce a
CSVFilterOutputStage object. Here are
all implementations in one chunk:
Example 8-10. Implementation of CSVFilterOutputStage
#include "CSVFilterOutputStageCreator.h"
#include "CSVFilterOutputStage.h"
CFilterOutputStage*
CSVFilterOutputStageCreator:: operator()(std::string type)
{
if (type == "csv") {
return new CSVFilterOutputStage;
} else {
return nullptr;
}
}
std::string
CSVFilterOutputStageCreator::document() const
{
return "csv - Comma separated fields";
}
CFilterOutputStageCreator*
CSVFilterOutputStageCreator::clone()
{
return new CSVFilterOutputStageCreator(*this);
}
To register this creator with the factory, modify
CMySpecTclApp::AddCommands to read:
void
CMySpecTclApp::AddCommands(CTCLInterpreter& rInterp)
{
CTclGrammerApp::AddCommands(rInterp);
CFilterOutputStageFactory::getInstance().Register(*(new CSVFilterOutputStageCreator));
}
I chose this method becaus I know that by the time
CTclGrammerApp::AddCommands has run,
the entire filter framework is set up because that call
registers the filter command ensemble.