読者です 読者をやめる 読者になる 読者になる

凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

C++(C)でgnuplotにパイプを繋げてグラフを簡単に描画Comments 其の二

gnuplot C++

昨日の記事 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出力を実装しようと思います.