2014-11-18 3 views
2
typedef void (*CALLBACK)(); 
class Filter 
{ 
public: 
    void callback() 
    { 
     cout << "callback" << endl; 
    } 
}; 

void SetCallback(CALLBACK pCallBack) 
{ 
    pCallBack(); 
} 



int main() 
{ 
    Filter f; 
    SetCallback(f.callback); 
} 

В основном, SetCallback (f.callback); заявление дает ошибку. Может ли кто-нибудь помочь мне исправить проблему?Установка функции обратного вызова, которая является нестационарной функцией-членом класса

+0

Ответ прост: вы не можете. –

+0

Конечно, вы можете. Возможно, с некоторым синтаксисом. См. Мой ответ ниже. Он работает, но может иметь недостатки, о которых я не знаю (поэтому я призываю других прокомментировать мой ответ, пожалуйста :) – Steve

ответ

1

Вы должны подумать о том, что такое обратный вызов и как называется функция-член.

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

Когда функция-член вызывается, ее первым параметром является указатель this, то есть ссылка на объект, на который вызывается метод.

Именно по этой причине не можно использовать метод-член как обратный вызов. Вы можете использовать только истинные функции или статические функции-члены, для которых не требуется специальный (неявный для программиста, но реальный в точке зрения компилятора) параметр this.

5

Проблема заключается в том, что функция-член не является простой функцией без параметров, потому что она всегда имеет неявный параметр this.

Если вы сталкиваетесь с устаревшим интерфейсом C, который требует простой функции обратного вызова без параметра пользовательского контекста (a void*, который функция просто переходит на обратный вызов), у вас есть проблема.

Если у вас есть пользовательский контекст, это легко. Передайте указатель на объект в зависимости от контекста, а также использовать функцию обертки в качестве фактического обратного вызова:

typedef void (*CALLBACK)(void*); 
class Filter 
{ 
public: 
    static void CallbackWrapper(void* context) { 
     static_cast<Filter*>(context)->callback(); 
    } 

private: 
    void callback(); 
}; 

int main() { 
    Filter f; 
    SetCallback(&Filter::CallbackWrapper, &f); 
} 

Если у вас нет контекста, вот несколько вариантов:

  • магазин объект в глобальную переменную и получить доступ к ней из оболочки. Это имеет очевидные недостатки использования глобальной переменной и не допускает более одного обратного вызова таким образом. Для длительных обратных вызовов это очень плохо.
  • Небольшое усовершенствование, приведенное выше, заключается в использовании локальной локальной переменной потока. Это интересно для плотно обработанных обратных вызовов, например. вы вызываете функцию, которая будет сразу использовать ваш обратный вызов несколько раз, а затем вернуться. Подумайте, qsort(). По крайней мере, таким образом, вы не получите проблемы безопасности потоков. Все еще не вариант для длительных обратных вызовов.
  • Наконец, опция, которая работает на большинстве платформ, но является большой работой, вы можете создать функцию заглушки во время выполнения, которая введет указатель на объект. Это в основном означает выделение части памяти, отключение защиты выполнения в этой памяти, если платформа использует это, и поместить там машинный код, который загружает указатель объекта и вызывает функцию на нем.

Окончательный варианта все еще имеет много недостатков: это очень зависит от платформы и может даже не работать вообще на некоторых (вы не можете отключить защиту исполнения в прошивке, AFAIK), это CPU-специфический (так как вы необходимо создать правильный код для каждого), и есть проблема управления памятью для заглушки. С другой стороны, иногда это единственное, что работает. Delphi делает такие вещи для своего окна и процедур крюка иногда, и ATL делает это тоже.

2

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

Для этого может потребоваться C++ 11.

#include <iostream> 
#include <string> 
#include <functional> 

using namespace std; 

struct MessageSource 
{ 
     function<void(const string& msg)> _callback; 

     template<typename A, typename B> 
     void connect(A func_ptr, B obj_ptr) 
     { 
       _callback = bind(func_ptr, obj_ptr, placeholders::_1); 
     } 

     void send_msg(const string& msg) 
     { 
       if (_callback) 
        _callback(msg); 
     } 

     void disconnect() 
     { 
       _callback = nullptr; 
     } 
}; 

struct Printer 
{ 
     void print(const string& msg) { std::cout << msg << std::endl; }; 
}; 

int main() 
{ 
     { 
       Printer p; 
       MessageSource s; 
       s.connect(&Printer::print, &p); 
       s.send_msg("test"); 
       s.disconnect(); 
       s.send_msg("test again"); 
     } 

     system("pause"); 
     return 0; 
} 
+0

Заинтересованы, почему это было приостановлено. Оно работает... – Steve

Смежные вопросы