昨日の記事 d:id:hecomi:20100707 の追加です.
コードのシンプル化
昨日のコードは,コンテナ版,配列版でほぼ同じコードを実行するという冗長なものでしたので,別にプロットするプライベートな関数plotX, plotXY, plotXYZへ投げるように変更しました.
また,プロット時に外部ファイルに書き出すか,それとも'-'オプションでgnuplot上でデータを入力するかというplot_typeという引数を設けていたのですが,replot出来ない点・コードが長くなるという点から,除去しました.
テンプレートテンプレート引数
メンバ関数Plotでは,テンプレートテンプレートパラメータを用いて,コンテナを識別しています.
/*! @brief 2次元要素を描画 */ template <class T, template <class A, class Allocator = std::allocator<A> > class Container> void Plot(Container<T> contX, Container<T> contY) { PlotInfo<Container<T>::iterator> piX = { contX.begin(), contX.end() }, piY = { contY.begin(), contY.end() }; PlotXY(piX, piY); } /*! @brief 2次元要素を描画(配列) */ template <class T, int N, int M> void Plot(T (&contX)[N], T (&contY)[M]) { PlotInfo<T*> piX = { &contX[0], &contX[N] }, piY = { &contY[0], &contY[M] }; PlotXY(piX, piY); }
一見,
/*! @brief 2次元要素を描画 */ template <class Container> void Plot(Container contX, Container contY) { PlotInfo<Container::iterator> piX = { contX.begin(), contX.end() }, piY = { contY.begin(), contY.end() }; PlotXY(piX, piY); } /*! @brief 2次元要素を描画(配列) */ template <class T, int N, int M> void Plot(T (&contX)[N], T (&contY)[M]) { PlotInfo<T*> piX = { &contX[0], &contX[N] }, piY = { &contY[0], &contY[M] }; PlotXY(piX, piY); }
でも良さそうに見えますが,後者のコードではオーバーロードが曖昧となってしまい,エラーとなってしまいます.
すなわち,
double arrX[100], arrY[100]; 〜 (arrX, arrYに代入する処理) 〜 Plot(arrX, arrY);
とすると,コンパイラは
Plot(double* contX, double* contY) // 前者のコード Plot(double[100] contX, double[100] contY) // 後者のコード
というコードのどちらにすればいいか判断ができないのです.したがって,明示的にテンプレートテンプレートパラメータを用いてコンテナと配列を識別しています.
(もっと良い方法があったら教えてください).
以下に,現在のソースコードを掲載します.
gnuplot.h
#pragma once /*! @file @brief gnuplotクラス @author hecomi @date July 5, 2010. */ #include <vector> #include <fstream> #include <string> /*! @brief gnuplotを扱うライブラリ gnuplotを扱うクラスに関するライブラリです. pgnuplotへとパイプを繋ぎ,関数やデータをgnuplotを用いてプロットする手助けをします. pgnuplot.exeが環境変数へと登録されている必要があります. */ class CGnuplot { private: /*! @brief パイプを繋げるファイルポインタ */ FILE* Fp; /*! @brief バッファフラッシュ */ void Flush(); /*! @brief beginとend格納用構造体 */ template <class Container> struct PlotInfo { Container begin; Container end; }; /*! @brief 1次元要素をプロットする */ template <class T> void PlotX(PlotInfo<T> x) { T it = x.begin; std::ofstream fout(TempFileName); while (it != x.end) { fout << *it << std::endl; it++; } Command("plot '%s' w lines", TempFileName); } /*! @brief 2次元要素をプロットする */ template <class T> void PlotXY(PlotInfo<T> x, PlotInfo<T> y) { T itX = x.begin, itY = y.begin; std::ofstream fout(TempFileName); while (itX != x.end && itY != y.end) { fout << *itX << " " << *itY << std::endl; itX++; itY++; } Command("plot '%s' w lines", TempFileName); } /*! @brief 3次元要素をプロットする */ template <class T> void PlotXYZ(PlotInfo<T> x, PlotInfo<T> y, PlotInfo<T> z) { T itX = x.begin, itY = y.begin, itZ = z.begin; std::ofstream fout(TempFileName); while (itX != x.end && itY != y.end && itZ != z.end) { fout << *itX << " " << *itY << " " << *itZ << std::endl; itX++; itY++; itZ++; } Command("splot '%s' w lines", TempFileName); } public: /*! @brief コンストラクタ */ CGnuplot(); CGnuplot(const char* file_name); /*! @brief デストラクタ */ ~CGnuplot(); /*! @brief 一時ファイル名 */ static const char* TempFileName; /*! @brief 正常に機能しているかどうか */ bool Check(); /*! @brief printfライクに指定のコマンドを実行 */ void Command(const char* format, ...); /*! @brief 関数を描画 */ void DrawFunc(const char* format); /*! @brief 1次元要素を描画 */ template <class T, template <class A, class Allocator = std::allocator<A> > class Container> void Plot(Container<T> cont) { PlotInfo<Container<T>::iterator> pi = { cont.begin(), cont.end() }; PlotX(pi); } /*! @brief 1次元要素を描画(配列) */ template <class T, int N> void Plot(T (&cont)[N]) { PlotInfo<T*> pi = { &cont[0], &cont[N-1] }; PlotX(pi); } /*! @brief 2次元要素を描画 */ template <class T, template <class A, class Allocator = std::allocator<A> > class Container> void Plot(Container<T> contX, Container<T> contY) { PlotInfo<Container<T>::iterator> piX = { contX.begin(), contX.end() }, piY = { contY.begin(), contY.end() }; PlotXY(piX, piY); } /*! @brief 2次元要素を描画(配列) */ template <class T, int N, int M> void Plot(T (&contX)[N], T (&contY)[M]) { PlotInfo<T*> piX = { &contX[0], &contX[N] }, piY = { &contY[0], &contY[M] }; PlotXY(piX, piY); } /*! @brief 3次元要素を描画 */ template <class T, template <class A, class Allocator = std::allocator<A> > class Container> void Plot(Container<T> contX, Container<T> contY, Container<T> contZ) { PlotInfo<Container<T>::iterator> piX = { contX.begin(), contX.end() }, piY = { contY.begin(), contY.end() }, piZ = { contZ.begin(), contZ.end() }; PlotXYZ(piX, piY, piZ); } /*! @brief 3次元要素を描画(配列) */ template <class T, int N, int M, int L> void Plot(T (&contX)[N], T (&contY)[M], T (&contZ)[L]) { PlotInfo<T*> piX = { &contX[0], &contX[N] }, piY = { &contY[0], &contY[M] }, piZ = { &contZ[0], &contZ[L] }; PlotXYZ(piX, piY, piZ); } /*! @brief Xラベルをセット */ void SetXLabel(const char* format); /*! @brief Yラベルをセット */ void SetYLabel(const char* format); /*! @brief Xプロット範囲を設定 */ void SetXRange(const double x_min, const double x_max); /*! @brief Yプロット範囲を設定 */ void SetYRange(const double y_min, const double y_max); /*! @brief リプロット */ void Replot(); /*! @brief EPS出力 */ /*! @brief マクロ機能 */ };
gnuplot.cppは前回同様です.
今後も研究の合間に育てていきたいと思います.次回はEPS出力を実装しようと思います.