Два класса (A, B) - B, чтобы иметь указатель на нестатический метод класса A - PullRequest
3 голосов
/ 10 февраля 2012

У меня есть два класса, которые ничего не знают о себе, класс A, класс B.

Класс А называется ДВИГАТЕЛЕМ, Класс B называется GUI.

Я хочу, чтобы в классе GUI был указатель на функцию в классе ENGINE, чтобы при возникновении события в GUIControl он вызывал функцию-член ENGINE с двумя параметрами (int, int).

Вот как бы я хотел это получить:

class CGUIManager
{
public:
    void SetControlCallback(void(*pFunctionPointer)(int,int) );

private:
    void (*m_pControlCallbackFunction)(int,int) ;
};
void CGUIManager::SetControlCallback(void(*pFunctionPointer)(int,int) )
{
    if(pFunctionPointer)
        m_pControlCallbackFunction = pFunctionPointer;
}

class CEngine
{
private:        
    void GUIControlsCallback(int iControlID, int iControlMessage);
    CGUIManager *pGUI;
};

Теперь при инициализации ДВИГАТЕЛЯ я хочу позвонить:

//Set Controls Callback to CEngine Method 
pGUI->SetControlsCallback( GUIControlsCallback );

Чтобы зарегистрировать обратный вызов в классе CGUIManager, который указывает на метод в классе CEngine.

Как я могу это сделать?

Заранее спасибо.

Ответы [ 4 ]

4 голосов
/ 10 февраля 2012

Я бы предложил использовать интерфейс (или что-то в этом роде), если вы хотите, чтобы он был oo вместо указателей на функции (которые должны указывать на статический член, кстати)

class IGuiCallback
{
public:
    virtual void  GUIControlsCallback(int iControlID, int iControlMessage)=0;
};

class CGUIManager
{
public:
    void SetControlCallback(IGuiCallback*);

private:
    IGuiCallback* m_pCallback;
};

class CEngine:public IGuiCallback
{
public:
    void GUIControlsCallback(int iControlID, int iControlMessage);

private:
    CGUIManager *pGUI;
};

затем в engine:

pGUI->SetCallback(this);

В моем коде могут быть некоторые синтаксические ошибки, но вы должны получить картинку

0 голосов
/ 10 февраля 2012

Если вы можете изменить интерфейс класса CGUIManager, я рекомендую вам обобщить его для использования boost::function<void(int, int)> (или std::function при записи в C ++ 11) вместо указателя функции.

Если вы не можете, к сожалению, вы жертва плохого дизайна.Обратные вызовы в стиле C, в которых используются указатели на функции, обычно позволяют некоторому параметру пользовательских данных void* переносить любую дополнительную информацию, связанную с обратным вызовом - в этом случае указатель CEngine может быть приведен к void * и свободная оболочка функцииможет быть написано, чтобы привести void* обратно к CEngine.Однако, если вы можете изменить интерфейс обратного вызова, лучше использовать функцию boost / STL.

0 голосов
/ 10 февраля 2012

Самый простой подход - использовать std::function<void(int, int) в качестве типа зарегистрированного обратного вызова: этот объект можно использовать для вызова любой функции [объекта], которая вызывается с двумя int с.В частности, он может вызвать функцию-член CEngine::GUIControlsCallback(int, int), которая на самом деле имеет три параметра:

  1. два очевидных параметра для функции-члена типа int
  2. неявный указатель наобъект (который становится this)

Способ, которым это сделано, состоит в создании функционального объекта, который в качестве первого параметра предоставляет указатель на объект CEngine и принимает два целых числа:

struct CEngine_bind {
    CEngine_bind(CEngine* engine): engine_(engine) {}
    void operator()(int i0, int i1) { this->engine_->GUIControlsCallback(i0, i1); }
    CEngine* engine_;
};

В качестве альтернативы, вы можете использовать std:bind(), который представляет собой функцию с соответствующей привязкой:

CEngine engine; // ... wherever this object is coming from)
std::function<void(int, int)> callback(std::bind(&CEngine::GUIControlsCallback, &engine,
    std::placeholders::_1, std::placeholders::_2));

... и затем установить объект callback в качестве обратного вызова.Этот объект просто вызывается, передавая два целочисленных параметра, которые вызовут функцию-член ссылочного объекта:

callback(10, 20);

вызовет

engine.GUIControlsCallback(10, 20);

std::function<void(int, int)> является копируемым,то есть вы можете легко хранить его в своем CGUIManager классе.

0 голосов
/ 10 февраля 2012

Указатели на функции-члены не являются указателями на функции в C ++.

Чтобы позвонить позже (используя предоставленную SetControlsCallback подпись), вызывающему необходимо иметь действительный экземпляр CEngine. Этого можно добиться, привязав указатель к CEngine к GUIControlsCallback:

CEngine* pEngine; // initialized somewhere
pGUI->SetControlsCallback(std::bind1st(pEngine, GUIControlsCallback));

Если вы используете Boost или C ++ 11, вам лучше использовать их версии привязок (boost::bind или std::bind соответственно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...