関数オブジェクトを作って遊ぼう

便利な自作ファンクタをいくつか作ったのでメモ。
ありそうでないので自作してみたよ。
mem_comp
任意型のオブジェクトについて、指定したメンバ、メソッドの値で、比較演算を行う。比較演算述語を指定することも可能。デフォルトは比較される対象の型の、lessが適用される。以下のようなことができる。

class A
{
  ..
  int Val1( void ){ return m_val1; }
  const int& Val2( void ) const { return m_val2; }
  ..
  int m_val1;
  int m_val2;
};
vector< pair< int, float > > v;
vector< A > va;
list< A > la;
//..
sort( v.begin(), v.end(), mem_comp( &pair< int, int >::first ) ); 
sort( v.begin(), v.end(), mem_comp( &pair< int, int >::second, greater<int>() ) ); 
sort( va.begin(), va.end(), mem_comp( &A::m_val1 ) );// public メンバ値でも
sort( va.begin(), va.end(), mem_comp( &A::Val1 ) );// public メソッド返値でも
sort( va.begin(), va.end(), mem_comp( &A::Val2, greater<int>() ) );// 参照型でも
la.sort( mem_comp( &A::Val2 ) ); // list::sortにも

よくオブジェクトの集合をそのメンバの値でソートしたいがために、専用の比較演算子をその場のローカル関数などとして作ったりしなくてはならない場合が結構あるわけだが、mem_compを使えばそういう手間がまるきり必要なくなる。
comp_first/comp_second
任意のpair型オブジェクトどうしを、firstあるいはsecondの値で、比較する。比較演算子は内部べた書きだけど、pairの完全な型をいちいち記述する必要がない。greater版で大きい順にもできる。以下のような使い方。

vector< pair< int, float > > v;
//あるいは
vector< pair< Hoge, Tako > > v;
// 要するに任意pair<>型のvector v があったとして、、
//..
sort( v.begin(), v.end(), comp_first() ); // firstの値でソート(小さい順)
sort( v.begin(), v.end(), comp_second() ); // secondの値でソート(小さい順)
sort( v.begin(), v.end(), comp_first_greater() ); // firstの値でソート(大きい順)
sort( v.begin(), v.end(), comp_second_greater() ); // secondの値でソート(大きい順)
// ↑このように任意のpair型要素を、簡単にソートできる!

後日記:pair型ではデフォルトのoperator<があり、それはfirstの値で大小比較を行ってくれるものだ。しかも、firstの値どうしが同じだった場合は、secondの値で比較してくれる。というものがあるので、上記、comp_firstなるものは全く持って必要ないはずなのであった。
comp_size
任意のstlコンテナ型オブジェクトどうしを、size()の値で、比較する。同じく比較演算子は内部べた書きだけど、コンテナ型の完全な型をいちいち記述する必要がない。greater版で大きい順にもできる。以下のような使い方。

vector< vector<int > > v;
//あるいは
vector< vector< Hoge > > v;
list< list< Hoge > > lst;
// 要するに任意pair<>型のvector v があったとして、、
//..
sort( v.begin(), v.end(), comp_size() ); // firstの値でソート(小さい順)
sort( v.begin(), v.end(), comp_size_greater() ); // firstの値でソート(大きい順)
lst.sort( comp_size() );
// ↑このように任意のvector型要素を、size()の値で簡単にソートできる!

以下、それぞれのファンクタのソースはこんな感じ。
これをヘッダーのどこかにこぴぺして、あとは使うだけ。
comp_first/comp_second.hpp(↓)

// pair<A,B>::first/second, size() の値で比較
// comp_first, comp_second, comp_size (C) 2009 nurs 
// 
#include <iostream>
#include <functional>
using namespace std;
#define POC_DEF_COMP_PAIR( xx, pt )\
class comp_##xx{ public: template< class PairTyp > bool operator()( PairTyp& a, PairTyp& b )\
{ return a.##xx##pt < b.##xx##pt; } };\
class comp_##xx##_greater{ public: template< class PairTyp > bool operator()( PairTyp& a, PairTyp& b )\
{ return a.##xx##pt > b.##xx##pt; } };
POC_DEF_COMP_PAIR(first,)// comp_first(_greater) を定義
POC_DEF_COMP_PAIR(second,)// comp_second(_greater) を定義
POC_DEF_COMP_PAIR(size,())// comp_size(_greater) を定義
#undef POC_DEF_COMP_PAIR

mem_comp.hpp(↓)

// メンバの値で比較
// mem_comp (C) 2009 nurs 
// 
#include <iostream>
#include <functional>
using namespace std;
template< class T > class poc_ct{ public: typedef T param_type; };
template< class T > class poc_ct<T&>{ public: typedef T param_type; };
#define POC_DEF_MEMCOMP_t( __n, __f, __cmp1, __cmpa, __cmpb, __cmp2 )\
template< class S, class T, class Pred=less<typename poc_ct<S>::param_type> > class __n##{ public:\
  explicit __n##( __cmp1 __f, Pred pred=less<typename poc_ct<S>::param_type>()):m_m( p ),m_pred( pred ){}\
  bool operator()( __f T& a, __f T& b)const{ return m_pred( __cmpa##, __cmpb );}\
  __cmp2 __f##; Pred m_pred; };\
template< class S, class T, class Pred > __n##< S, T, Pred > mem_comp( __cmp1 __f, const Pred& pred ){ return __n##< S, T, Pred >(p, pred); }\
template< class S, class T > __n##< S, T > mem_comp( __cmp1 __f ){ return __n##< S, T >(p); }
POC_DEF_MEMCOMP_t(mem_comp_t, ,S T::*p, a.*m_m, b.*m_m, S T::*m_m )
POC_DEF_MEMCOMP_t(mem_comp2_t, ,S (T::*p)(void), (a.*m_m)(), (b.*m_m)(), S (T::*m_m)(void) )
POC_DEF_MEMCOMP_t(mem_comp2c_t, const, S (T::*p)(void), (a.*m_m)(), (b.*m_m)(), S (T::*m_m)(void) ) 
#undef POC_DEF_MEMCOMP_t//

ちなみにBoostのラムダ式とか使って、同じよなことできるという噂も。でもそれほどスマートな感じには書けなかったりするみたい。でもラムダ式ラムダ式で素敵。
mem_compでも、メンバのメンバを参照して比較とかはさすがにできない。