Design: How to manage output in a simulation

Hi, I'm writing a numerical simulation and I'm trying to keep the code as generic as possible in order to be able to adapt it and reuse parts of it later. I'm looking for a suitable way to manage the output of intermediate results and the progress of my algorithm.
My first approach was just to have one class that organizes the output streams. However, this class needs to collect data from several classes that are used in the algorithm - so the output commands start to clutter my whole code.
Can someone suggest a design or a tool that would help me manage the output?
You could use a generic interface for each type of calculation you do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Algorithm
{
    std::vector<double> inputs;
    std::vector<double> output;

public:
    void AddInput(double in) { inputs.push_back(in); }
    std::vector<double> GetOutput() { return outputs; }
    void Process()
    {
        for (int i = 0; i < inputs.size(); ++i)
            outputs.push_back( ProcessOne(inputs[i]) );
    }

protected:
    virtual double ProcessOne(double in) = 0;
};


Then you could have a class for each algorithm:
1
2
3
4
5
6
7
8
class NewtonsMethod : public Algorithm
{
protected:
    double ProcessOne(double in)
    {
        ... Algorithms here
    }
};

or
1
2
3
4
5
6
7
8
9
10
11
12
13
class Derivative : public Algorithm
{
    double m_lastIn;
public:
    Derivative() : m_lastIn(0) {}
protected:
    double ProcessOne(double in)
    {
        double diff = in - m_lastIn;
        m_lastIn = in;
        return diff;
    }
}



They will all share a common interface now, and you could even make a vector of pointers to each algorithm and treat them in the same way. FOr example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{
    typedef std::vector<Algorithm*> Algorithms;
    Algorithms algorithms;

    algorithms.push_back( new NewtonsMethod() );
    algorithms.push_back( new SecantMethod() );
    algorithms.push_back( new FirstOrderFilter() );

    while (true)
    {
        double in;
        std::cin >> in;
        if (in == 0.0) break;
        
        for (Algorithms::iterator it = algorithms.begin(); it !+ algorithms.end(); ++it)
            (*it)->AddInput(in);
    }

    for (Algorithms::iterator it =algorithms.begin(); it != algorithms.end(); ++it)
        (*it)->Process();


}
Last edited on
Great, that should work - thanks a lot for your detailed answer!
One more question regarding this design: I would like my simulation code to save detailed intermediate results to text files. I need more information than just the output vetors for each algorithm step, i.e. I'd like each algorithm to save certain intermediate steps.

Of course I could have an ostream that is passed as a reference to each algorithm; then the algorithm simply writes to the stream. However, I don't think this is a good choice - maybe at some point I'd like to reuse the algorithms without any output which would require me to change the implementation of the whole algorithm.

Can you suggest a nice way to do this and keep the implementation of the algorithm independent from the choice which results I'd like to save to a text file? Should I use some kind of logging tool (I've never done this)?
I would define my own class for IO. I made one at one point, but can't find it now. I overloaded the << and >> operators so it could be used just like an ostream object. It was something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Logger
{
  int m_target;
  std::ofstream fout;
public: 
  static const int FILE = 0x1;
  static const int CONSOLE = 0x2;

  Logger(std::string file) : fout(file), m_target(0) {}

  Logger& SetTarget(int target) { m_target = target; return *this; }
  Logger& operator()(int target) { return SetTarget(target); }

  template <class T> 
  Logger& operator<<(T& input)
  {
    if (m_target & FILE) 
      fout << input;
    if (m_target & CONSOLE)
      std::cout << input;
    return *this;
  }
};


Usage:
1
2
3
4
5
Logger log("logfile.log");

log(FILE | CONSOLE) << "Hello Everyone";
log(FILE) << "Hello file";
log(CONSOLE)  << "Hello console";

> Of course I could have an ostream that is passed as a reference to each algorithm;
> then the algorithm simply writes to the stream.
> However, I don't think this is a good choice - maybe at some point I'd like to reuse the algorithms
> without any output which would require me to change the implementation of the whole algorithm.

This is the tailor-made scenario to use the null object pattern.
http://sourcemaking.com/design_patterns/null_object

For example, using boost::iostreams

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/null.hpp>
#include <fstream>
#include <cassert>

void some_algorithm( std::ostream& log_file, const char* test_message )
{
    log_file << "test message: " << test_message << '\n' ;
    assert( log_file.good() ) ;
}

int main()
{
    std::ofstream logfile( "log.txt" ) ;

    using namespace boost::iostreams ;
    null_sink null ;
    stream<null_sink> null_ostream(null) ;

    some_algorithm( std::cout, "this is logged to stdout" ) ;

    some_algorithm( logfile, "this is logged to the file log.txt" ) ;

    some_algorithm( null_ostream, "this is not logged anywhere" ) ;
}

http://coliru.stacked-crooked.com/a/14aedb46070e2a9f
Topic archived. No new replies allowed.