みぃちゃんの頭の中はおもちゃ箱

略してみちゃばこ。泣いたり笑ったり

メンバ関数を呼び出す出力反復子 (C++)

2014年09月11日 21時30分00秒 | IT・デジタル
以前にC++で作った出力反復子を汎用化しました。指定したオブジェクトの指定したメンバ関数に引数1個を渡して呼び出す出力反復子です。
template< class Target,
	class RetType,
	class Par1Type,
	class BaseIter = std::iterator< std::output_iterator_tag, void, void, void, void>
	>
struct UnaryMemberCallIterator_cref
	: public std::iterator_traits< BaseIter>
{ public:
 typedef RetType (Target::*func_type)( const Par1Type &);
 UnaryMemberCallIterator_cref( Target &trg, func_type f)
				: trg_( trg), func_( f) { }
 UnaryMemberCallIterator_cref &operator =( const Par1Type &x)
				{	(trg_.*func_)( x);
					return *this;
				}
 UnaryMemberCallIterator_cref &operator * ( void) { return *this; }
 UnaryMemberCallIterator_cref &operator ++( void) { return *this; }
 UnaryMemberCallIterator_cref &operator ++( int ) { return *this; }
protected:
 Target &trg_;
 func_type func_;
};

この出力反復子に書き込むと、その値が引数として渡されてメンバ関数が呼び出されます。

*out++ = x; // (trg_.*func_)(x) が呼び出される。

この反復子クラス (UnaryMemberCallIterator_cref) を直接使おうとしても使えるのですが、

class Hoge
{
public:
void some_func( const SomeRecord &i);
};

Hoge hoge;
copy( container.begin(), container.end(),
UnaryMemberCallIterator_cref< Hoge, void, SomeRecord>( hoge, &Hoge::some_func));

戻り型と引数の型をテンプレート引数で指定しなければならないのは美しくありません (copyでループするだけならこのような反復子を作る必要はありませんが、簡単な例として示しました。本来は、呼び出し先の関数が複数の結果を出力するため、結果を回収する手段として作った出力反復子を一般化したものです)。

で、ヘルパ関数を用意する、と。

template< class Target, class RetType, class Par1Type>
UnaryMemberCallIterator_cref< Target, RetType, Par1Type>
unary_member_call_iterator( Target &trg, RetType (Target::*f)( const Par1Type &))
{
return UnaryMemberCallIterator_cref< Target, RetType, Par1Type>( trg, f);
}

本体よりも戻り値や引数の宣言のほうが長い、ステキな関数に。

Targetがコンテナでなければ基底クラスBaseIterに意味はないので、ヘルパ関数はBaseIterを省略するバージョンのみを用意しました。Targetがコンテナの場合にそのTarget::iteratorを基底クラスとする出力反復子を生成するには、別のヘルパ関数 (unary_member_call_container_iteratorなど) を作る必要があります。

これですっきり書けるようになりました。

copy( container.begin(), container.end(),
unary_member_call_iterator( hoge, &Hoge::some_func));

ヘルパ関数を一段かませないといけないのが何だかまどろっこしいけど。

UnaryMemberCallIterator_crefはメンバ関数の引数型がconst Par1Type &の場合のクラスです。ここまで来たら、値渡し版の反復子クラスや非const版の反復子クラスも用意しておきたい。そうなれば対応するヘルパ関数も。