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