7

プロジェクトに単純なメッセージング システムを追加しようとしています。このシステムでは、関数によってイベントを呼び出すことができます。これにより、そのイベントに登録されているすべてのコールバックが呼び出されます。

これを行う論理的な方法は、関数ポインターを使用することです。登録のために、目的のコールバック関数へのポインターをイベント マネージャーに渡すことは簡単に可能です。イベント コールバック関数は常に を返し、引数としてintを受け取ります。void*

ただし、静的グローバル関数をイベント コールバックとして登録したくありません。クラス メンバー関数を使用して登録したいと考えています。

  • C++でこれを達成することさえ可能ですか? 異なるクラスのメンバー関数へのポインターを格納および呼び出しますが、関数ヘッダーは同じです。

  • これが不可能な場合、これを回避する方法について何か提案はありますか? イベントリスナーをクラスに直接追加したいと思っています。

4

7 に答える 7

8

はい、可能です。 C++0x にはfunctionthis を処理するクラスがあり、他の人が指摘しているように Boost にも同様の機能があります。

自分でロールすることもできますが、構文は気弱な人向けではありません。

#include <iostream>

class Callable
{
    public:

        virtual ~Callable() {}
        virtual int operator() (void* args) = 0;
};

class CallableFreeFunction  : public Callable
{
    public:

        CallableFreeFunction(int (*func)(void*)) : func_(func) {}

        virtual int operator() (void* args) { return (*func_)(args); }

    private:

        int (*func_)(void*);
};

template <typename tClass>
class ClassMemberCallable : public Callable
{
    public:

        ClassMemberCallable(tClass* instance, int (tClass::*memberfunction)(void*)) : instance_(instance), memberfunc_(memberfunction) {}

        virtual int operator() (void* args) { return (instance_->*memberfunc_)(args); }

    private:

        tClass* instance_;
        int (tClass::*memberfunc_)(void*);
};

class Foo
{
    public:

        int derp(void* args)
        {
            std::cout << args << '\n';
            return 2;
        }
};

int freefunctionfoo(void* args)
{
    std::cout << "free" << args << '\n';
    return 2;
}

int main(int argc, char* argv[])
{
    Foo myfoo;

    Callable* callable = new ClassMemberCallable<Foo>(&myfoo, &Foo::derp);

    (*callable)(0);

    delete callable;

    callable = new CallableFreeFunction(freefunctionfoo);

    (*callable)(0);

    delete callable;

    std::cin.get();

    return 0;
}

これは、フリー関数とメンバー関数の両方を不透明な方法で処理する方法を示しています。これは単純な例であり、さまざまな方法でより一般的かつ堅牢にすることができます。構文のヘルプについては、次のページを参照してください。

http://www.newty.de/fpt/index.html

http://www.parashift.com/c++-faq-lite/pointers-to-members.html

より多くのアイデアについては、これを見ることもお勧めします。

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

于 2011-03-03T13:53:58.980 に答える
3

もちろん可能です!Boost.Signal2Boost.Bindを見てください。

Boost.Signal2 は基本的に、まさに必要なものであるシグナルとスロット システムを実装します。次に、boost::bindwhich を一般化したものをstd::bind1st使用してstd::bind2nd、基本的に考えられるものすべて (この場合はメンバー メソッド) に関数オブジェクト ラッパーを取得できます。本当にパワフルです。

この公式のブースト チュートリアルを参照してください。

于 2011-03-03T12:51:35.123 に答える
2

これは、そのような仕事をするための私のあまり良い試みではありません: まず第一に、基本イベント ハンドラー クラスが必要です。今のところ、それを EvtHandler と呼びましょう:

class Event; //implement this yourself, it shall contain general but good info about event

class EvtHandler
{
public:
    virtual void handleEvent (Event & evt);
};

次に、何らかの方法でイベントを処理することになっているすべてのクラスは、このクラスから派生する必要があり、同じデータ型 (この場合はvoid )を返し、同じパラメーターを受け取る限り、必要なだけ新しい関数を実装できます。 (この場合のイベント)。このような:

class Foo : public EvtHandler
{
public:
    void handleFooEvent (Event & event);
};

次に、特別なイベントごとにメッセージ センターを実装しました。これは、リスナーを登録し、必要に応じてイベントをディスパッチする必要がありました。

class ShutdownMessageCenter
{
typedef std::map<EventHandler *, event_func> ListenerMap; 
public:

    void register (EvtHandler * handler, void(EvtHandler::*memFunc)(Event &)) {
        m_lmap[handler] = memFunc;
    }

     void callListeners () {
         Event shutdown_event (EM_SHUTDOWN /*just imagine this can mean something, idk*/);
         ListenerMap::iterator itr = m_lmap.begin ();
         for (; itr != m_lmap.end(); ++itr) {
            EvtHandler * handler = itr->first;
            void (EvtHandler::*func)(Event &) = itr->second;
            (handler->*func)(shutdown_event);
          }
      }

private:
   ListenerMap m_lmap;
};

次に、たとえば、EvtHandlers をこの特定のメッセージ センターに登録できます。

ShutdownMessageCenter message_center;
EvtHandler * some_handler = new EvtHandler ();
Foo * some_foo = new Foo ();
message_center.register (some_handler, &EvtHandler::handleEvent);
message_center.register (some_foo, static_cast<void (EvtHandler::*)(Event &)>(&Foo::handleFooEvent);
message_center.callListeners ();

しかし、もう一度言いますが、これはまったく良くないので、共有したいと思いました! 混乱してすみません、ハハ!

于 2011-03-03T14:04:40.883 に答える
1

何をアーカイブしたいのか完全にはわかりませんが、Boost Signals2を見る必要があるかもしれません

ある種のシグナル/スロット メカニズムを作成したい場合に非常に役立ちます。

于 2011-03-03T12:54:27.927 に答える
0

No, it is not possible (unless you do c++/cli with .net).

Now, you can still create static functions, pass them an object as a parameter, and the only thing that they'll do is call your member function on that object. (Actually a cast will be required first).

于 2011-03-03T12:49:30.527 に答える
0

私が管理した最も近いものは、静的メンバー関数をコールバックとして登録することです。静的メンバーは、イベント ハンドラーによって送信される引数に加えて、オブジェクト (this) ポインターを引数として取り、これを使用してメンバー関数を呼び出します。

class myClass{
public:
  static void callback(void *arg, void *obj)
  {
    if (obj)
      reinterpret_cast<myClass*>(obj)->cb(arg);
  }

private:
  void cb(void *arg);
};

ハンドラーに登録myClass::callbackします。this返される内容が制限されている場合は、arg が参照する構造体でこれをラップする必要がある場合があります。

于 2011-03-03T12:54:21.353 に答える