function4-mem_fun

前回function3-mem_fun - while( c++ );の修正。

以下のように、

  • Tの参照
  • Tへのポインタ
  • shared_ptr< T >

を受け取って、すべて同一の書き方で呼び出せるようなmem_funを作ります。

class Hoge
{
public:
    int func( int, float )
    {
        return 1;
    }
};

template< typename T, typename F >
int piyo( T& t, F f )
{
    return f( t, 0, 0.0f );
}

template< typename T, typename F >
int piyo( T* t, F f )
{
    return f( t, 0, 0.0f );
}

template< typename T, typename F >
int piyo( shared_ptr< T >& t, F f )
{
    return f( t, 0, 0.0f );
}


int main()
{
    Hoge hoge;
    piyo( hoge, mem_fun( &Hoge::func ) );
    piyo( &hoge, mem_fun( &Hoge::func ) );
    shared_ptr< Hoge > p( new Hoge ); 
    piyo( p, mem_fun( &Hoge::func ) );

    return 0;
}

mem_fun2

とりあえず戻り値R、引数2つのmem_funを作りますね。

//R:戻り値の型
//T:クラスT
//A1:引数1の型
//A2:引数2の型
template< typename R, typename T, typename A1, typename A2 >
class mem_fun2
{
    typedef R ( T::*func_t )( A1, A2 );
private:
    func_t f;

public:
    mem_fun2( func_t f )
        : f( f )
    {}
};
operator()

関数オブジェクトとして呼び出すので、それぞれTのポインタ、Tの参照、テンプレートUを引数にもつoperator()を定義します。

template< typename R, typename T, typename A1, typename A2 >
class mem_fun2
{
/*...*/
public:
    R operator()( T* t, A1 a1, A2 a2 )
    {
        return ( t->*f )( a1, a2 );
    }
    
    R operator()( T& t, A1 a1, A2 a2 )
    {
        return ( t.*f )( a1, a2 );
    }
    
    template< typename U >
    R operator()( U& u, A1 a1, A2 a2 )
    {
        return ( (*u).*f )( a1, a2 );
    }
};
mem_funヘルパー関数

使いやすいようにヘルパー関数を用意します。

template< typename R, typename T, typename A1, typename A2 >
mem_fun2< R, T, A1, A2 > mem_fun( R ( T::*f )( A1, A2 ) )
{
    return mem_fun2< R, T, A1, A2 >( f );
}
2個以上の引数

preprocessorで2個以上(0個〜N個)の引数にも対応させましょう。
以下の4箇所をpreprocessorで書き換えることが出来れば楽が出来そうです。

template< typename R, typename T, typename A1, typename A2, ... typename N >
    typedef R ( T::*func_t )( A1, A2, ... AN );
    R operator()( T* t, A1 a1, A2 a2, ..., AN aN )
        return ( t->*f )( a1, a2, ..., aN );
pp_comma_if

Nが0の時、カンマが必要ないので、以前preprocessor - while( c++ );で作ったpp_ifで分岐しましょう。

#define pp_empty()
#define pp_comma() ,

//cond != 0 -> ,
//cond == 0 -> なし
#define pp_comma_if( cond ) pp_if( cond, pp_comma, pp_empty )()
pp_template_params
template< typename R, typename T, typename A1, typename A2, ... typename N >

これを以下のように書き換えます。

template< typename R, typename T pp_comma_if( N ) pp_template_params( N ) >

以前preprocessor - while( c++ );で作ったpp_repeatを使って、繰り返しましょう。

//pp_param( A, 0 ) -> A1
//pp_param( A, 1 ) -> A2
#define pp_param_n( p, n ) pp_cat( p, pp_inc( n ) )
//pp_enum_param( 0, A ) -> A1
//pp_enum_param( 1, A ) -> , A2
//pp_enum_param( 2, A ) -> , A3
#define pp_enum_param( n, p ) pp_comma_if( n ) pp_param_n( p, n )
//pp_enum_params( 0, A ) -> なし
//pp_enum_params( 1, A ) -> A1
//pp_enum_params( 2, A ) -> A1, A2
//pp_enum_params( N, A ) -> A1, A2, ... AN
#define pp_enum_params( n, p ) pp_repeat( n, pp_enum_param, p )

//pp_template_params( 0 ) -> なし
//pp_template_params( 1 ) -> typename A1
//pp_template_params( 2 ) -> typename A1, typename A2
//pp_template_params( N ) -> typename A1, typename A2, ..., typename AN
#define pp_template_params( n ) pp_enum_params( n, typename A )
template< typename R, typename T pp_comma_if( 0 ) pp_template_params( 0 ) >
                ↓展開すると
template< typename R, typename T >


template< typename R, typename T pp_comma_if( 2 ) pp_template_params( 2 ) >
                ↓展開すると
template< typename R, typename T, typename A1, typename A2 >
pp_template_args

同様に、

    typedef R ( T::*func_t )( A1, A2, ... AN );

これを以下のように書き換えます。

    typedef R ( T::*func_t )( pp_template_args( N ) );

pp_template_paramsで作成済みですね。

/*...*/

//pp_template_args( 0 ) -> なし
//pp_template_args( 1 ) -> A1
//pp_template_args( 2 ) -> A1, A2
//pp_template_args( N ) -> A1, A2, ..., AN
#define pp_template_args( n ) pp_enum_params( n, A )
pp_function_args

次は、これを

        return ( t->*f )( a1, a2, ..., aN );

以下のように書き換えます。

        return ( t->*f )( pp_function_args( N ) );

pp_template_argsと同じです。

/*...*/

//pp_function_args( 0 ) -> なし
//pp_function_args( 1 ) -> a1
//pp_function_args( 2 ) -> a1, a2
//pp_function_args( N ) -> a1, a2, ..., aN
#define pp_function_args( n ) pp_enum_params( n, a )
pp_function_params

最後に、これを

    R operator()( T* t, A1 a1, A2 a2, ..., AN aN )

以下のように書き換えます。

    R operator()( T* t pp_comma_if( N ) pp_function_params( N ) )

ちょっと面倒です。

//pp_param2( 0 ) -> A1 a1
//pp_param2( 1 ) -> A2 a2
#define pp_param_n2( n ) pp_cat( A, pp_inc( n ) ) pp_cat( a, pp_inc( n ) )
//pp_enum_param2( 0 ) -> A1 a1
//pp_enum_param2( 1 ) -> , A2 a2
//pp_enum_param2( 2 ) -> , A3 a3
#define pp_enum_param2( n ) pp_comma_if( n ) pp_param_n2( n )
//pp_enum_params2( 0 ) -> なし
//pp_enum_params2( 1 ) -> A1 a1
//pp_enum_params2( 2 ) -> A1 a1, A2 a2
//pp_enum_params2( N ) -> A1 a1, A2 a2, ..., AN aN
#define pp_enum_params2( n ) pp_repeat( n, pp_enum_param, pp_empty )

完成

Nに引数の個数を指定して、必要な分だけコピペします。

#define N /*適当な値*/

template< typename R, typename T pp_comma_if( N ) pp_template_params( N ) >
class pp_cat( mem_fun, N )
{
    typedef R ( T::*func_t )( pp_template_args( N ) );
private:
    func_t f;

public:
    pp_cat( mem_fun, N )( func_t f )
        : f( f )
    {}

public:
    R operator()( T* t pp_comma_if( N ) pp_function_args( N ) )
    {
        return ( t->*f )( pp_function_args( N ) );
    }
    
    R operator()( T& t pp_comma_if( N ) pp_function_args( N ) )
    {
        return ( t.*f )( pp_function_args( N ) );
    }
    
    template< typename U >
    R operator()( U& u pp_comma_if( N ) pp_function_args( N ) )
    {
        return ( (*u).*f )( pp_function_args( N ) );
    }
};

#undef N

pp_inc(追記)

インクリメントした値を返す

#define pp_inc( n ) pp_inc_n( n )
#define pp_inc_n( n ) pp_cat( pp_inc_, n )

#define pp_inc_0 1
#define pp_inc_1 2
/*...*/
#define pp_inc_255 256

サンプルのダウンロード