7

注: タグからすでに明らかなように、これは厳密に C ++ 03です。 はい、私は知っています、ラムダはこのすべての苦痛を取り除きます(そして新しい種類をもたらすでしょう)が、これは組み込みシステムです、 90年代のOSバージョンで、C ++ 03コンパイラ(GCC4.1.x、BTW)、またはC++コンパイラを持っていてよかったと言われています。C++11の投稿は控えてください。解決策。実際にこする必要はありません。
また、もちろん、なども実際には含まstd::bind()れていますが、コードにノイズを追加するだけだと思ったので、プレフィックスを編集しました std::function()std::tr1tr1
。 )

関数を登録する必要があるサーバーのようなものがいくつかあり、オブジェクトの類似しているがわずかに異なる関数を呼び出すようにそれらを適応させる必要があります。これらの関数には異なる引数リストがあります。std::functionサーバーはそれを「認識」しており、関数を登録しようとすると、テンプレート引数として渡されたマジックタグに応じて、正しい署名(つまり、必要に応じて正しい)を持つ関数のみを受け入れます。

コードのスケッチは次のとおりです

// this I just use
class server {
public:
    template<unsigned int MagicTag>
    bool register_call(typename some_traits_type<MagicTag>::func_type);
};

// this needs to be called from the server
class X {
public:
    bool foo();
    bool bar(std::string&);
    bool baz(int);
};

// this is the glue
class Y {
public:
    Y(X& x) : x_(x) {
        register_call<MAGIC_FOO>(&Y::foo    );
        register_call<MAGIC_BAZ>(&Y::bar, _1);
        register_call<MAGIC_FBZ>(&Y::baz, _1);
    }

private:
    X&                    x_;

    template<unsigned int MagicTag, typename Function>
    bool register_call(Function function) {
        somewhere->register_call<MagicTag>(std::bind( function
                                                    , this ));
    }
    template<unsigned int MagicTag, typename Function, typename PlaceHolder1>
    bool register_call(Function function, PlaceHolder1 place_holder1) {
        somewhere->register_call<MagicTag>(std::bind( function
                                                    , this
                                                    , place_holder1 ));
    }

    int foo()               {return x_.foo()  ? MAGIC_OK : MAGIC_FAILED;}
    int bar(std::string& s) {return x_.bar(s) ? MAGIC_OK : MAGIC_FAILED;}
    int baz(int i)          {return x_.baz(i) ? MAGIC_OK : MAGIC_FAILED;}
};

これは実際には機能しますが、実際にはもっと多くの機能があり、退屈なコピーアンドペーストの努力としてこれを行うと、私の尊厳感が損なわれ、臭いコードが生成されます。これらの関数はすべてまったく同じように機能しますが、唯一の違いは、呼び出す関数と、渡す引数または渡さない引数だけです。これらを1つのパラメーター化された関数に折りたたんで、違いを隠しておくことができますstd::bind()。これに失敗したので、私は最初に(のように)パラメーターなしですべての関数に対してこれを行うことに決めましたfoo()。これは圧倒的多数です。

foo()そのため、 -like関数のすべての呼び出しを、面倒な部分をX実行する単一の関数を介してルーティングしたいと思いました。Y::call_it

int call_it(std::function<bool()> f) {return f() ? MAGIC_OK : MAGIC_FAILED;}

X適切な関数を引数としてバインドします。

register_call<MAGIC_FOO>(&X::foo); // note the X!

// (this is wrong)
somewhere->register_call<MagicCode>( std::bind( std::bind( &Y::call_it
                                                         , this
                                                         , function )
                                              , std::ref(x_) );

明らかに、これは間違っており、これを解決するための他のすべての試みも間違っています。(私は今10週間しか遊んでいstd::bind()ないので、我慢してください)。std::function結局、私は、成長した男を涙で倒すことができ、少なくとも1年間は縮小と彼の拡大家族を養うはずの、テンプレート化された内臓からの陽気なエラーメッセージの信じられないほどの迷路に迷いました。

それで、私が完全な欲求不満から自分自身を殺し、私の子供たちを孤児にする前に、どうすればこれを行うことができますか?

4

2 に答える 2

3

私が集めたものから、あなたは適切にバインドさY::call_it()れたオブジェクトで呼び出したいと思います。std::function内部関数が取る引数の数が異なることを考えると、std::function<bool()>追加の引数が渡される場合のジェネレーターを作成する必要があります。X登録時にオブジェクトをバインドできると仮定すると、追加の引数なしでメンバー関数を登録するのは簡単です。

template <int Magic, typename RC>
void register_call(RC (X::*member)()) {
    somewhere->register_call<Magic>(
        std::bind(&Y::call_it, this,
             std::function<bool()>(std::bind(member,
                                             std::ref(this->x_)))));
}

1つ以上の引数を渡す場合std::function<bool()>、追加の引数をバインドする必要があるため、呼び出し時にオブジェクトを作成する必要があります。これはヘルパー関数なしでは実行できないと思いますが、引数の数ごとに1つのヘルパー関数を使用して実行できます。

template <typename RC, typename Arg0>
static std::function<bool()>
bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) {
    return std::bind(member, std::ref(x), arg0);
}
template <int Magic, typename RC,
          typename Arg0, typename PlaceHolder>
void register_call(RC (X::*member)(Arg0), PlaceHolder pc) {
    somewhere->register_call<Magic>(
        typename some_traits_type<Magic>::type(
            std::bind(&Y::call_it, this,
                      std::bind(&bind_argument<RC, Arg0>, member,
                                std::ref(this->x_), pc))));
}

ヘルパー関数は、追加の引数がバインドされた関数を作成します。バインドされている関数は、レジスタ関数で期待されるものと同じタイプになるように構築されていることに注意してください。これは、たとえば、追加の無視された引数を取る関数を作成するために必要です。

以下は、コンパイルされるかどうかを確認するために使用したテストプログラムです。私は手元にTR1を備えたC++2003コンパイラを持っておらず、C++2011コンパイラを使用してコードをコンパイルしました。ただし、TR1を使用したC++2003では使用できないC++2011拡張機能を使用したことはないと思います。

#include <functional>

enum {
    MAGIC_OK,
    MAGIC_FAILED,
    MAGIC_FOO,
    MAGIC_BAR,
    MAGIC_FBZ,
    MAGIC_BAZ
};

template <int> struct server_traits;
template <> struct server_traits<MAGIC_FOO> {
    typedef std::function<bool()> type;
};
template <> struct server_traits<MAGIC_BAR> {
    typedef std::function<bool(std::string&)> type;
};
template <> struct server_traits<MAGIC_FBZ> {
    typedef std::function<bool(long)> type;
};
template <> struct server_traits<MAGIC_BAZ> {
    typedef std::function<bool(std::string, long)> type;
};


// this I just use
class server {
public:
    template<unsigned int MagicTag>
    bool register_call(typename server_traits<MagicTag>::type) {
        return true;
    }
};

server  s;
server* somewhere = &s;

// this needs to be called from the server
class X {
public:
    bool foo() { return true; }
    bool bar(std::string&) { return true; }
    bool baz(int) { return true; }
};

// this is the glue
class Y {
public:
    Y(X& x) : x_(x) {
        register_call<MAGIC_FOO>(&X::foo    );
        register_call<MAGIC_BAR>(&X::bar, std::placeholders::_1);
        register_call<MAGIC_FBZ>(&X::baz, std::placeholders::_1);
        register_call<MAGIC_BAZ>(&X::baz, std::placeholders::_2);
    }

private:
    X& x_;

    int call_it(std::function<bool()> f) {
        return f() ? MAGIC_OK : MAGIC_FAILED;
    }

    template <int Magic, typename RC>
    void register_call(RC (X::*member)()) {
        somewhere->register_call<Magic>(
            std::bind(&Y::call_it, this,
                 std::function<bool()>(std::bind(member,
                                                 std::ref(this->x_)))));
    }
    template <typename RC, typename Arg0>
    static std::function<bool()>
    bind_argument(RC (X::*member)(Arg0), X& x, Arg0 const& arg0) {
        return std::bind(member, std::ref(x), arg0);
    }
    template <int Magic, typename RC,
              typename Arg0, typename PlaceHolder>
    void register_call(RC (X::*member)(Arg0), PlaceHolder pc) {
        somewhere->register_call<Magic>(
            typename server_traits<Magic>::type(
                std::bind(&Y::call_it, this,
                          std::bind(&bind_argument<RC, Arg0>, member,
                                    std::ref(this->x_), pc))));
    }
};

int main()
{
    X x;
    Y y(x);
}
于 2012-12-26T22:22:45.617 に答える
1

チャットチャネルのリンクから、ネストされたバインドがコンパイルされないという事実に問題を要約したように見えます。

bind( &Y::call_it, this, bind( &X::foo, ref(x_) ) )

コンパイラは内部bind()の型シグネチャを推測できないため(この場合はfunction <bool()>として)。これは代わりに機能する可能性があります。

bind( &Y::call_it, this, function<bool()>( bind( &X::foo, ref(x_) ) ) )

もしそうなら、あなたは次のようなものを持っているでしょう

template<unsigned int MagicTag, typename Function >
bool register_call(Function func) {
    somewhere->register_call<MagicTag>(
      bind( &Y::call_it, this, function<bool()>( bind( func, ref(x_)))));
}

どういうわけか、2番目のテンプレートパラメータは必要ないかもしれないと感じますが。重要なアイデアは、2つのstd::bindの間にstd::functionを配置することです。

于 2012-12-27T20:25:39.973 に答える