C ++ Functor Callback Setup - PullRequest
       27

C ++ Functor Callback Setup

1 голос
/ 26 ноября 2010

Я следую учебнику Ларса Хенделя Functor на newty.de, чтобы настроить систему обратного вызова.Однако я немного сбит с толку и надеюсь, что кто-то может мне помочь.

Вот мой шаблон 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.(), и когда вызывается игровое событие, я могу пройти через список || моих хуков, проверить, совпадает ли имя события, а затем выполнить обратные вызовы по мере необходимости.Что меня смущает, так это то, как я могу сохранить обратный вызов в моей структуре HookEvent, которая выглядит следующим образом.

std::vector<EventHook*> m_EventHooks;

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

Пока я закомментировал это, но я уверен, что очевидно, с чем я запутался и гдеоблажаюсьЕсли кто-то может оказать какую-либо помощь, он будет очень признателен.

Ответы [ 4 ]

3 голосов
/ 26 ноября 2010

Большинство людей не понимают наследство. Как правило, производные классы являются деталями реализации. Единственный раз, когда вы произносите их имена, это строить их. Кроме того, виртуальные функции в базе должны быть частными и чистыми и должны быть полностью недоступны в производных классах, это ошибка проектирования в C ++, которая не применяется принудительно.

struct TBaseCallback
    void operator()(IGameEvent *pEvent) { _Call(pEvent); }; 
    void Exec(IGameEvent *pEvent) { _Call(PEvent); }
private:
    virtual void _Call(IGameEvent *pEvent)=0;
};

struct EventHook
{
    char *name;
    EventHookMode mode;
    TBaseCallback *p;
    void dispatch(char *msg; IGameEvent *e) const { 
      if(strcmp(msg,name)==0) p->Exec(e); 
   }
};

При таком дизайне не имеет значения, что находится в классах, производных от TBaseCallback, и не должно. Только абстракция должна быть публично видимой. В обычном коде это трудно реализовать ... когда вы используете DLL для получения производных классов, это абсолютно обязательно, потому что набор производных классов является открытым / произвольным / бесконечным / неопределенным (выбирайте сами).

Кстати: когда вы подтолкнете это к более сложным абстракциям, вы скоро обнаружите, почему объектная ориентация является нарушенной концепцией. С загруженными из DLL производными классами вы просто не можете использовать читы с ключами dynamic_cast (потому что они закрыты / специфичны / конечны / детерминированы).

0 голосов
/ 26 ноября 2010

Я думаю, что вы получите сложную ошибку компилятора, потому что вы используете T * вместо T в своем экземпляре шаблона.

Попробуйте это:

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

должно скомпилироваться, если эточто ты хочешь.

0 голосов
/ 26 ноября 2010

Проблема, которую я вижу (и вполне могут быть и другие), заключается в том, что в типе pEventCallback параметр шаблона должен быть типом класса, но на самом деле это тип указателя.Одно из исправлений (не ограничивающее, какие типы переносит обратный вызов) - это использование базового типа:

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

Если есть еще что-то для API TEventCallback, и он должен быть доступен через 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::Call Звоните TBaseCallback::operator().

void TBaseCallback::Call(IGameEvent *pEvent) { this->operator()(pEvent); };
0 голосов
/ 26 ноября 2010

Класс, который будет выполнять обратные вызовы, должен содержать список вызываемых объектов 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(), будет названа правильная версия класса.

...