function7-placeholder

前回function6-bind - while( c++ );の続き。
preprocessorを使うのはある程度完成に近づいてからのほうが良さそうなので、先にplaceholderに対応させます。

今日の目標

  • placeholderを使えるように。

class fuga
{
public:
    int func( int, float )
    {
        std::cout << "fuga" << std::endl;
        return 2;
    }
};

int main()
{
    bind( &fuga::func, new fuga, 0, 1.0f )();
    int x =0;
    bind( &fuga::func, _1, _2, 1.0f )( new fuga, x );

    return 0;
}

bind生成時ではなく、bindのoperator()呼び出し時に、引数を渡すことが可能です。これでstd::for_eachと一緒に使うことも出来ますね。

    std::vector< fuga* > a;
    a.push_back( new fuga );
    a.push_back( new fuga );
    std::for_each( a.begin(), a.end(),
        xtl::bind( &fuga::func, _1, 0, 1.0f )
        );

ということで、今回は_1、_2のようなplaceholderを渡せるようにbind、arg_listNを修正していきます。

arg

boost::bindで使用可能な_1、_2などのplaceholderは以下のようにanonymous-namespaceで定義されています。

namespace
{

/*...*/

static boost::arg<1> _1;
static boost::arg<2> _2;
static boost::arg<3> _3;
static boost::arg<4> _4;
static boost::arg<5> _5;
static boost::arg<6> _6;
static boost::arg<7> _7;
static boost::arg<8> _8;
static boost::arg<9> _9;

/*...*/

} // unnamed namespace

テンプレート引数に整数値を渡して、arg< 1 >〜arg< 9 >まで別の型として定義されています。


このboost::argと同等のargを作ってみましょう。

template< int N >
struct arg{};

以上です。簡単ですね。型を定義するだけなのでこれでOKです。

boostではplaceholderかどうか調べるためのメタ関数が用意されていますが、現状で必要ないので無視しておきます。

同様にplacceholder(_1〜_9)も作っておきます。boostと同じanonymous-namespaceに作るので、boostと同時に使うことは出来ません。

namespace{

    static arg< 1 > _1;
    static arg< 2 > _2;
    static arg< 3 > _3;
    static arg< 4 > _4;
    static arg< 5 > _5;
    static arg< 6 > _6;
    static arg< 7 > _7;
    static arg< 8 > _8;
    static arg< 9 > _9;
}


では、いきなり使ってみましょうか。

int hoge( int, float )
{
    std::cout << "hoge" << std::endl;
    return 1;
}

int main()
{
    int x = 0;
    bind( hoge, _1, 1.0f )( x );
    return 0;
}

コンパイルエラーです。。。



前回function6-bind - while( c++ );の自作bindは以下のようになっていました。

//グローバル関数用
template<
    typename R		//戻り値の型
    , typename A1	//引数1の型
    , typename A2
    >
bind_t< R, R ( * )( A1, A2 ), arg_list2< R, A1, A2 > >
bind( R ( *f )( A1, A2 ), A1 a1, A2 a2 )
{
    typedef R result_type;
    typedef result_type ( *func_type )( A1, A2 );
    typedef arg_list2< R, A1, A2 > arg_list;
    typedef bind_t< result_type, func_type, arg_list > bind_type;

    return bind_type( f, arg_list( a1, a2 ) );
}

bindの引数の関数ポインタ型がint (*)( arg<1>, float )になり、関数hogeを渡せなくなるようですね。関数の引数の型とbindの引数の型は異なるものにしたほうが良さそうです。

//グローバル関数用
template<
    typename R		//関数の戻り値の型
    , typename B1	//関数の引数1〜Nの型
    , typename B2
    , typename A1	//引数1〜Nの型
    , typename A2
    >
bind_t< R, R ( * )( B1, B2 ), arg_list2< R, A1, A2 > >
bind( R ( *f )( B1, B2 ), A1 a1, A2 a2 )
{
    typedef R result_type;
    typedef result_type ( *func_type )( B1, B2 );
    typedef arg_list2< R, A1, A2 > arg_list;
    typedef bind_t< result_type, func_type, arg_list > bind_type;

    return bind_type( f, arg_list( a1, a2 ) );
}

コンパイルすると、、、


bindは大丈夫ですね。ただ、arg_list2でコンパイルエラーになります。

//引数2
/*...*/
class arg_list2
/*...*/
{
/*...*/
public:
    template< typename F >
    result_type operator()( F& f )
    {
        return f( a1, a2 );//←コンパイルエラー
    }
};

今回fにはhogeが渡されているので、第1引数はintです。しかし、a1にはplaceholderの_1が格納されています。当然エラーですね。

  • aNがplaceholderであれば、bind_t::operator()の対応する引数を、
  • そうでなければ、aNをそのまま渡す

以上の点を修正する必要があります。

arg_list

  • bind_t::operator()の引数を受け取る
  • aNがplaceholder→受け取った引数
  • aNがplacholderでない→aNをそのまま

オーバーロードで解決できそうです。

//引数2
/*...*/
class arg_list2
/*...*/
{
/*...*/
public:
    template< typename FuncType, typename ArgList >
    result_type operator()( FuncType& f, ArgList& al )
    {
        return f( al.get( a1 ), al.get( a2 ) );
    }
public:
    //引数がplaceholderであればメンバのaNを返す
    A1 get( arg< 1 >& )const{ return a1; }
    A2 get( arg< 2 >& )const{ return a2; }
    //placeholderでなければそのまま返す
    template< typename T > T get( T t )const{ return t; }
};

同様に、arg_list0〜ard_list3を修正します。

//引数なし
template< typename R >
class arg_list0
{
protected:
    typedef R result_type;
public:
    template< typename FuncType, typename ArgList >
    result_type operator()( FuncType& func, ArgList& /*al*/ )
    {
        return func();
    }
public:
    //引数がplaceholderでなければそのまま返す
    template< typename T > T get( T t )const{ return t; }
};

//引数1
template<
    typename R
    , typename A1
    >
class arg_list1
    : public arg_list0< R >
{
protected:
    A1 a1;
public:
    arg_list1( A1 a1 )
        : arg_list0()
        , a1( a1 )
    {}
public:
    template< typename FuncType, typename ArgList >
    result_type operator()( FuncType& func, ArgList& al )
    {
            return func( al.get( a1 ) );
    }
public:
    //引数がplaceholderであればメンバのaNを返す
    A1 get( arg< 1 >& )const{ return a1; }
    //placeholderでなければそのまま返す
    template< typename T > T get( T t )const{ return t; }
};

//引数2
template<
    typename R
    , typename A1
    , typename A2
    >
class arg_list2
    : public arg_list1< R, A1 >
{
protected:
    A2 a2;
public:
    arg_list2( A1 a1, A2 a2 )
        : arg_list1( a1 )
        , a2( a2 )
    {}
public:
    template< typename FuncType, typename ArgList >
    result_type operator()( FuncType& func, ArgList& al )
    {
        return func( al.get( a1 ), al.get( a2 ) );
    }
public:
    //引数がplaceholderであればメンバのaNを返す
    A1 get( arg< 1 >& )const{ return a1; }
    A2 get( arg< 2 >& )const{ return a2; }
    //placeholderでなければそのまま返す
    template< typename T > T get( T t )const{ return t; }
};

//引数3
template<
    typename R
    , typename A1
    , typename A2
    , typename A3
    >
class arg_list3
    : public arg_list2< R, A1, A2 >
{
protected:
    A3 a3;
public:
    arg_list3( A1 a1, A2 a2, A3 a3 )
        : arg_list2( a1, a2 )
        , a3( a3 )
    {}
public:
    template< typename FuncType, typename ArgList >
    result_type operator()( FuncType& func, ArgList& al )
    {
        return func( al.get( a1 ), al.get( a2 ), al.get( a3 ) );
    }
public:
    //引数がplaceholderであればメンバのaNを返す
    A1 get( arg< 1 >& )const{ return a1; }
    A2 get( arg< 2 >& )const{ return a2; }
    A3 get( arg< 3 >& )const{ return a3; }
    //placeholderでなければそのまま返す
    template< typename T > T get( T t )const{ return t; }
};

bind_t

class bind_t
{
/*...*/
public:
    R operator()()
    {
        arg_list0< R > al;
        return l( f, al );
    }

    template< typename A1 >
    R operator()( A1& a1 )
    {
        arg_list1< R, A1 const& > al( a1 );
        return l( f, al );
    }

    template< typename A1, typename A2 >
    R operator()( A1& a1, A2& a2 )
    {
        arg_list2< R, A1 const&, A2 const& > al( a1, a2 );
        return l( f, al );
    }

    template< typename A1, typename A2, typename A3 >
    R operator()( A1& a1, A2& a2, A3& a3 )
    {
        arg_list2< R, A1 const&, A2 const&, A3 const& > al( a1, a2, a3 );
        return l( f, al );
    }
};

実行結果

int hoge( int x, float y )
{
    std::cout << "hoge:" << x << "," << y << std::endl;
    return 1;
}

int main()
{

    int x = 1;
    float y = 2.0f;
    bind( hoge, 0, 1.0f )();
    //bind( hoge, 0, 1.0f )( 0 );//定数は不可
    bind( hoge, 0, 1.0f )( x, y );//placeholderがなければ無視される
    bind( hoge, _1, 1.0f )( x );
    bind( hoge, _1, _2 )( x, y );
    bind( hoge, x, _1 )( y );

    return 0;
}
  • bind_t::operator()で定数は渡せない(C++言語自体の制約?)

次回は

  • preprocessorで2個以上引数に対応
  • function+bind

の予定です。
正直preprocessorが面倒ですね。
全部マクロにしてしまうとデバッグしづらいですし。
boostでは一部perlを使ってソースを作っているようなので、私もperlを使ってみようと思っています。ちなみにperlの経験は、数年前に1日だけCGIで遊ぶときにかじった程度です。


これでようやくfunctionの続きに入れそうです。

download

  • bind20081216.zip 直
  • VC++2008ExpresEdition用
  • サンプルではxtlというnamespaceを使っています。eXtensive Template Library的な何か。