Boost.Fusionのススメ

必要に迫られて*1一部を使ってみたので紹介.

Boost.Fusionは,型の集合とそのインスタンスをどちらも操作することが出来るライブラリです.(適当)
MPLと同じような名前のメタ関数 / 型 / 関数が定義されており,現に非常に基礎的な事はどちらを使ってもできたりします.

fusionの利点

ちょっと触っただけですがfusionの利点は恐らく,
値から推論させた型を引き回して,好きな箇所で取り出すことが出来ることではないかと思います.

単純な例

typedef fusion::vector<int    , double  , string  > value_type;	// fusionのvector
typedef fusion::vector<int_<0>, int_<1> , int_<2> > index_type; // mpl::int_のvector

struct print {
	template< typename T >
	void operator() ( T const & t ) const {
		std::cout << t << ",";
	}
	~print() { std::cout << std::endl; }
};
value_type v( 3, 3.14, "eldesh" );
fusion::for_each(v, print());		// 3 ,3.14 ,eldesh ,
index_type i;
fusion::for_each(i, print());		// 0 ,1 ,2 ,

vectorの中身を表示してます. 見たまんまですね.

fusion::for_each(zip(i,t), print());	// (0 3) ,(1 3.14) ,(2 eldesh) ,

ZipWith的なもの. それっぽく表示してくれます.
型を明示してないのでメタメタして見えませんが,型を表示してみると意外と大変な事態になっています.

cout << typeid(zip(i,t)).name();
struct boost::fusion::zip_view,struct boost::mpl::int_<1>,struct boost::mpl::int_<2>,struct boost::fusion::void_,struct boost::fusion::
void_,struct boost::fusion::void_,struct boost::fusion::void_,struct boost::fusion::void_,struct boost::fusion

void_,struct boost::fusion::void_> const &,struct boost::fusion::vector

...(略)...

読みにくいですが, zipの型の中でもまだ最初に作ったfusion::vectorの型が保持されています.
つまりテンプレートを通してこの型にアクセスすれば, 始めに定義してからコード上に出なくなっている型を自由に取り出すことが出来ます.

暗黙に引き回している型を取り出す

// 2tupleの後ろの値を取り出して2倍する
cout << fusion::transform(zip(i,v), SecondDouble()) << endl;	// (6 6.28319 eldesheldesh)

struct SecondDouble {
	template< typename Sig >
	struct result;

	template< typename U >		// 戻り型を計算する
	struct result<SecondDouble(U)>	// transformはresult<struct(arg)>::type形式で戻り型を計算する(みたい)
		: public remove_reference<				// '&'を取り去る
			typename result_of::value_at_c<U,1>::type	// 2tupleの後ろの型
		>
	{};

	template< typename Pair >
	typename result<SecondDouble(Pair)>::type // 上で作ったので自分で使う
//	typename remove_reference< typename result_of::value_at_c<Pair,1>::type >::type 
	operator() (Pair const & t) const {
		return at_c<1>(t) + at_c<1>(t);	// vectorの2番目の'値'を取り出して弄る
	}
};

取り出すためのメタ関数が提供されています.

結果を格納

fusion::vector<int,double,string> ret = fusion::transform(zip(i,v), SecondDouble());

元と同じ型に戻すことが出来ました\(^o^)/

実はzip()はzip_view<>, transform()はtransform_view<>という型を返すので,以下のようにも受け取ることが出来ます.

fusion::transform_view<
	fusion::zip_view<
		fusion::vector<index_type const&, value_type const&>
	>
	, SecondDouble
> ret = fusion::transform(zip(i,v), SecondDouble());

以上!

今日はこんなモンで.

*1:なんとなく出来ないとイヤという理由が一番大きいけど