パイプストリームクラス

gnuplot のようなプログラムには,パイプを使ってほかのプログラムからデータを送ることができる.std::cout などのストリームクラスのように,パイプストリームクラスを作成すると便利そうなので,実装してみた.

パイプストリームクラス (pstream.h)

#ifndef pstream_h
#define pstream_h
//-------------------------------------------------------------------------------------------
#include <cstdio>
#include <iostream>
#include <sstream>
//-------------------------------------------------------------------------------------------
namespace hogehoge
{
//-------------------------------------------------------------------------------------------
namespace ps
{
//-------------------------------------------------------------------------------------------
struct __pendl {char dummy;} endl;
struct __pflush {char dummy;} flush;

class pipestream
{
private:
  FILE  *p;
public:
  pipestream (void) : p(NULL) {};
  pipestream (const char *name) : p(NULL) {open(name);};
  ~pipestream (void) {close();}
  bool open (const char *name)
    {
      if (is_open())  close();
      p = popen(name, "w");
      if (p == NULL)
      {
        std::cerr<<"failed to open "<<name<<std::endl;
        return false;
      }
      return true;
    };
  void close (void)
    {
      if(is_open())  {pclose(p); p=NULL;}
    };
  bool is_open (void) const {return p!=NULL;};
  template <typename T>
  pipestream& operator<< (const T &rhs)
    {
      std::stringstream ss;
      ss<<rhs;
      fputs(ss.str().c_str(), p);
      return *this;
    };
  pipestream& operator<< (const char *rhs)
    {
      fputs(rhs, p);
      return *this;
    };
  pipestream& operator<< (const __pendl &rhs)
    {
      fputs("\n", p);
      fflush(p);
      return *this;
    };
  pipestream& operator<< (const __pflush &rhs)
    {
      fflush(p);
      return *this;
    };
};
//-------------------------------------------------------------------------------------------
}  // end of namespace ps
//-------------------------------------------------------------------------------------------
}  // end of namespace hogehoge
//-------------------------------------------------------------------------------------------
#endif // pstream_h

サンプルプログラム

gnuplot でリサージュカーブを描かせるサンプル.

#include "pstream.h"
#include <fstream>
#include <cmath>
using namespace std;
using namespace hogehoge;

const double step_size (0.1);

void generate_lissajous (const char *filename, const double &time)
{
  ofstream ofs(filename);
  for (double t(time); t<time+1.0; t+=step_size)
    ofs<< cos(1.5*t) <<"  "<< sin(t) <<endl;
  ofs.close();
}

int main()
{
  const char *filename= "data.dat";
  double time=0.0;
  generate_lissajous (filename, time);

  ps::pipestream gnuplot ("gnuplot");

  gnuplot<<"set grid"<<ps::endl;
  gnuplot<<"set nokey"<<ps::endl;
  gnuplot<<"set xlabel 'X'"<<ps::endl;
  gnuplot<<"set ylabel 'Y'"<<ps::endl;
  gnuplot<<"set xrange[-1.2:1.2]"<<ps::endl;
  gnuplot<<"set yrange[-1.2:1.2]"<<ps::endl;
  gnuplot<<"plot '"<<filename<<"' w l lt 3"<<ps::endl;

  for (time+=step_size; time<20.0; time+=step_size)
  {
    generate_lissajous (filename, time);
    gnuplot<<"replot"<<ps::endl;
    usleep(50000);
  }
}

gnuplot のプロンプトでタイプするように,ストリーム演算子でコマンドを送れるので,コマンドを覚えている人は使いやすいかも.これをさらにラップしたグラフプロットクラスを作っても,そのクラスの使い方を覚える手間が掛かるし,凝ったことができないかもしれないし.このプログラムだと一石二鳥では.