3

ゲームのイベント システムを作成しようとしています。イベント マネージャーが格納するコールバックは、単純な関数とファンクターの両方にすることができます。また、関数/ファンクターを比較できるようにする必要があるため、イベントマネージャーから切断する必要があるものを知ることができます。

• 最初は boost::function; を使用してみました。operator== がないことを除いて、関数とファンクターを完全に適切に処理するため、必要に応じてコールバックを削除することはできません。

class EventManager
{
    typedef boost::function<void (boost::weak_ptr<Event>)> Callback;
    std::map<Event::Type, std::vector<Callback>> eventHandlerMap_;
};

• boost::signal も使用してみましたが、operator== に関連するコンパイルの問題も発生します。

バイナリ '==' : 'const Functor' 型の左側のオペランドを取る演算子が見つかりません (または、受け入れ可能な変換がありません)

void test(int c) {
    std::cout << "test(" << c << ")";
}

struct Functor
{
    void operator()(int g) {
        std::cout << "Functor::operator(" << g << ")";
    }
};

int main()
{
    boost::signal<void (int)> sig;

    Functor f;

    sig.connect(test);
    sig.connect(f);

    sig(7);

    sig.disconnect(f); // Error
}

これを実装する方法について他に何か提案はありますか? または、boost:: 関数または boost::signal を機能させるにはどうすればよいでしょうか。(ただし、アイテムの小さなコレクションでは信号がかなり遅いと聞いたので、boost:: 関数を使用したいと思います。)


編集:これは、EventManager に持たせたいインターフェイスです。

class EventManager
{
  public:
    void addEventHandler(Event::Type evType, Callback func);
    void removeEventHandler(Event::Type evType, Callback func);

    void queueEvent(boost::shared_ptr<Event> ev);
    void dispatchNextEvent();
};
4

4 に答える 4

1

ほとんどの汎用関数ラッパーは、関数の等価性をサポートしていないことがわかります。

どうしてこれなの?さて、そこであなたのファンクターを見てください:

struct Functor
{
    void operator()(int g) {
        std::cout << "Functor::operator(" << g << ")";
    }
};

これFunctorには がないoperator==ため、等しいかどうかを比較できません。したがって、boost::signal 値によって渡すと、新しいインスタンスが作成されます。これは、ポインターが等しいかどうかを false と比較し、値が等しいかどうかをテストする演算子はありません。

実際、ほとんどのファンクタには値等価述語がありません。あまり役に立ちません。これに対処する通常の方法は、代わりにコールバックへのハンドルを持つことです。boost::signals は、そのconnectionオブジェクトでこれを行います。たとえば、ドキュメントから次の例を見てください。

boost::signals::connection c = sig.connect(HelloWorld());
if (c.connected()) {
// c is still connected to the signal
  sig(); // Prints "Hello, World!"
}

c.disconnect(); // Disconnect the HelloWorld object
assert(!c.connected()); c isn't connected any more

sig(); // Does nothing: there are no connected slots

これにより、シグナル登録を直接参照しているため、HelloWorldは必要ありません。operator==

于 2011-07-30T15:19:48.580 に答える
1

Don Clugston の「メンバー関数ポインターと最速の C++ デリゲート」を検討することを強くお勧めします。記事を見つけて、ここからコードをダウンロードできます。

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

他の多くの利点の中でも、彼のデリゲートはすぐに使える比較演算子 (==、!=、<) を提供します。私は現在それらをリアルタイム システムに使用しており、あらゆる点で優れていると感じています。コンパイラの移植性の問題を修正するためにマイナーな変更を加える必要があったことを覚えているようです。ただし、そのエクスペリエンスはプラットフォームなどによって異なります。

また、この記事は数年前のものであるため、問題が発生した場合は、このデリゲートの実装に関する更新されたコード/ディスカッションについてグーグルで検索することをお勧めします。

于 2011-07-31T02:53:06.273 に答える
1

libsigc と libsigc++ を試したことがありますか? 私は Linux でそれらを使い始め、それらに恋をしました。現在、Windows アプリケーションでもそれらを使用しています。ブーストよりも拡張性と柔軟性が高いと思います。実装も簡単です。

于 2011-07-30T16:03:24.117 に答える
1

とにかく、私は解決策を見つけました。ちょっとしたテンプレート マジックで物事はシンプルになります(r):

template<typename F>
void EventManager::removeEventHandler(Event::Type evType, F func)
{
    auto compare = [func](const Callback& other) -> bool {
        F const* f = other.target<F>();
        if (f == nullptr) return false;
        return *f == func;
    };

    std::vector<Callback>& callbacks = ...;
    auto pend = std::remove_if(callbacks.begin(), callbacks.end(), compare);
    callbacks.erase(pend, callbacks.end());
}


template<typename R, typename F, typename L>
void EventManager::removeEventHandler(
    Event::Type evType, const boost::_bi::bind_t<R, F, L>& func)
{
    auto compare = [&func](const Callback& other) -> bool {
        auto const* f = other.target<boost::_bi::bind_t<R, F, L>>();
        if (f == nullptr) return false;
        return func.compare(*f);
    };

    std::vector<Callback>& callbacks = ...;
    auto pend = std::remove_if(callbacks.begin(), callbacks.end(), compare);
    callbacks.erase(pend, callbacks.end());
}

Boost.Bind オブジェクトを個別に処理する必要があるのoperator==は、実際には Bind オブジェクトの比較を行わず、他の 2 つの結果を比較する新しいファンクターを生成するためです (続きを読む)。Boost.Bind を比較するには、メンバー関数を使用する必要がありますcompare()

この型は Boost の内部型のようです(boost::_bi::bind_t名前空間 '_bi' のアンダースコアが意味するものだと思います)。boost::function_equal

このコードは、比較をoperator==行う定義がある限り、または Boost.Bind を使用している場合、すべてのタイプのファンクターで機能します。(C++0x)を表面的に調べてみましたが、比較できるものではないようで、上記のコードでは機能しません。 std::bind

于 2011-07-31T16:02:07.330 に答える