8.2. Example filter formatter.

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:

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:

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.