1

コールバック システムをセットアップするために、newty.deの Lars Haendel のFunctor チュートリアルに従っています。私は少し混乱していますが、誰かが私を助けてくれることを願っています.

これが私のFunctorテンプレートです

#include <igameevents.h>

// Abstract Base Class (Functor)
class TBaseCallback
{
public:

    // two possible functions to call member function. virtual cause derived
    // classes will use a pointer to an object and a pointer to a member function
    // to make the function call
    virtual void operator()(IGameEvent *pEvent){};  // call using operator
    virtual void Call(IGameEvent *pEvent) {};       // call using function
};


// Derived Template Class
template <class TClass> class TEventCallback : public TBaseCallback
{
private:

    void (TClass::*funcPtr)(IGameEvent*);       // pointer to member function
    TClass* thisPtr;                            // pointer to object

public:

    // constructor - takes pointer to an object and pointer to a member and stores them in two private variables
    TEventCallback(TClass* _thisPtr, void(TClass::*_funcPtr)(const char*))
    { thisPtr = _thisPtr;  funcPtr=_funcPtr; };

    // override operator "()"
    virtual void operator()(IGameEvent *pEvent)
    { (*thisPtr.*funcPtr)(pEvent); };           // execute member function

    // override function "Call"
    virtual void Call(IGameEvent *pEvent)
    { (*thisPtr.*funcPtr)(pEvent); };           // execute member function
};

私がやりたいことは、基本的に他の .dll が私の HookGameEvent() 関数を使用できるようにすることです。ゲーム イベントが呼び出されたときに、フックの vector||list を実行し、イベント名が一致するかどうかを確認してから、必要に応じてコールバックします。私を混乱させているのは、このような HookEvent 構造体にコールバックを格納する方法です。

std::vector<EventHook*> m_EventHooks;

struct EventHook
{
    char *name;
    EventHookMode mode;
    //TEventCallback<IGameEvent*> pEventCallback;
};

今のところコメントアウトしていますが、何が混乱しているのか、どこで失敗しているのかは明らかです。誰かが何か援助を提供できるなら、それは大歓迎です。

4

4 に答える 4

4

ほとんどの人は継承を理解していません。一般に、派生クラスは実装の詳細です。あなたがそれらの名前を口にするのは、それらを構築するときだけです。さらに、ベース内の仮想関数はプライベートで純粋である必要があり、派生クラスでは完全にアクセスできない必要があります。これが強制されないのは C++ の設計上のバグです。

TBaseCallback 構造体
    void operator()(IGameEvent *pEvent) { _Call(pEvent); };
    void Exec(IGameEvent *pEvent) { _Call(PEvent); }
プライベート:
    virtual void _Call(IGameEvent *pEvent)=0;
};

構造体 EventHook
{
    char *name;
    EventHookMode モード。
    TBaseCallback *p;
    void ディスパッチ(char *msg; IGameEvent *e) const {
      if(strcmp(msg,name)==0) p->Exec(e);
   }
};

この設計では、TBaseCallback から派生したクラスの内容に違いはありません。抽象化のみが公開されるべきです。通常のコードでは、これを強制するのは困難です.. DLL を使用して派生クラスを取得する場合、派生クラスのセットはオープン/任意/無限/不確定であるため、絶対に必須です (選択してください)。

ところで: これをより複雑な抽象化にプッシュすると、オブジェクト指向が壊れた概念である理由がすぐにわかります。DLL がロードされた派生クラスを使用すると、dynamic_cast スイッチでごまかすことはできません(それらは閉じている/特定の/有限/確定しているため)。

于 2010-11-26T04:48:58.140 に答える
0

私が目にする問題 (そして他にもある可能性が非常に高い) は、pEventCallbackの型では、テンプレート パラメーターはクラス型である必要がありますが、実際にはポインター型であるということです。1 つの修正 (コールバックがラップする型を制限しない) は、基本型を使用することです。

struct EventHook
{
    char *name;
    EventHookMode mode;
    TBaseCallback* pCallback;
};

TEventCallbackの API に他にもあり、 を介してアクセスできるようにする必要がある場合は、オブジェクトとそのメソッドを処理するEventHookのコードを別のサブクラスに移動する必要があります。TEventCallback

// Example EventCallback that takes other args
class EventCallback : public TBaseCallback {
public:
    EventCallback();
    EventCallback(const EventArgs& evtArgs);
    // EventCallback specific methods ...
    virtual EventArgs& args();
    virtual const EventArgs& args() const;
}

/* TReturn allows for calling methods with a non-void return. Return value is ignored. 
 */
template <class TClass, typename TReturn = void> 
class TMethodCallback : public EventCallback
{
private:
    typedef TReturn (TClass::*TMeth)(IGameEvent*);
    TMeth funcPtr;       // pointer to member function
    TClass* thisPtr;                            // pointer to object

public:

    // constructor - takes pointer to an object and pointer to a member and stores them in two private variables
    TMethodCallback(TClass* _thisPtr, TMeth _funcPtr)
    { thisPtr = _thisPtr;  funcPtr=_funcPtr; };

    // override operator "()"
    virtual void operator()(IGameEvent *pEvent)
    { (*thisPtr.*funcPtr)(pEvent); };           // execute member function

    // override function "Call"
    virtual void Call(IGameEvent *pEvent)
    { (*thisPtr.*funcPtr)(pEvent); };           // execute member function
};

オフトピック

TBaseCallback::Callcallのデフォルトの実装を作成することもできますTBaseCallback::operator()

void TBaseCallback::Call(IGameEvent *pEvent) { this->operator()(pEvent); };
于 2010-11-26T04:34:01.133 に答える
0

コールバックを行うクラスは、呼び出される Functor オブジェクトのリストを保持する必要があります。これらはあなたの

std::vector<EventHook*> m_EventHooks;

これで、EventHook に仮想関数が必要になります。

struct EventHook
{
    ...
    virtual void notifyMe();
}

次に、通知を受けることに関心のあるすべての人が、フックの独自の実装を作成します。

struct MyEventHook : public EventHook
{
    virtual void notifyMe() { ... whatever I want to do in that case ... }
}

驚異的なポリモーフィズムにより、m_EventHooks コンテナーのすべての要素を反復処理してnotifyMe()それらを呼び出すと、正しいクラスのバージョンが呼び出されます。

于 2010-11-26T04:29:20.523 に答える
0

テンプレートのインスタンス化で T の代わりに T* を使用しているため、複雑なコンパイラ エラーが発生すると思います。

これを試して:

struct EventHook
{
    char *name;
    EventHookMode mode;
    TEventCallback<IGameEvent> pEventCallback;
}; 

それがあなたが望むなら、コンパイルする必要があります。

于 2010-11-26T10:12:26.970 に答える