Передать любую функцию-член любого класса как функцию обратного вызова. - PullRequest
4 голосов
/ 04 мая 2011

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

Сейчас я делаю это так:

#define BUTTONCALLBACK(Func) bind1st( mem_fun( &ClassICanSupport::Func ), this )

Затем я могу создать такую ​​кнопку:

Button* b = new Button("Bla", BUTTONCALLBACK(functionIWanttoCall));

Функция обратного вызова имеет следующую подпись:

void callback(Button* source);

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

У меня быловзгляните на boost :: bind, но я не смог найти способ решить проблему.Кроме того, все мои классы являются производными от класса Object, поэтому я подумал о void *, который я мог преобразовать в нужный класс с помощью некоторого взлома typeid, но я не смог заставить его работать.В конце у меня всегда была проблема, заключающаяся в том, что я не мог полностью исключить тип класса функции обратного вызова (который был бы необходим для сохранения указателя функции в моем классе кнопок) и все еще иметь возможность вызывать функцию.

У вас есть идеи, как решить эту проблему?

Ответы [ 4 ]

3 голосов
/ 04 мая 2011

Не используйте указатели, используйте boost::function вместе с boost::bind (или std::function и std::bind, если C ++ 0x), что-то вроде

// in Button class (or whatever signature you need)
Button(const std::string&, boost::function<void(Button*)> callback) // ...
// you can then use callback as a function

// in calling code
Button *b = new Button("..", boost::bind(&Class::func, this));
1 голос
/ 04 мая 2011

Ну, самый простой способ - использовать виртуальные функции, если вы не хотите использовать Boost или не имеете доступа к C ++ 0x.

#include <iostream>

// fwd declare
class Button;

class BtnCallbackBase{
public:
  virtual void operator()(Button*) = 0;
};

template<class C>
class BtnCallback : public BtnCallbackBase{
private:
  typedef void (C::*callback_func)(Button*);

  C* _object;
  callback_func _onclick;

public:
  BtnCallback(C* obj, callback_func func)
    : _object(obj)
    , _onclick(func)
  {}

  virtual void operator()(Button* btn){
    (_object->*_onclick)(btn);
  }
};


class Button{
public:
  Button()
    : _onclick(0)
  {}

  void Click(){
    if(_onclick != 0)
      (*_onclick)(this);
  }

  template<class C>
  void RegisterCallback(C* obj, void (C::*func)(Button*)){
    // cleanup old callback, deleting null pointer is a noop
    delete _onclick;
    _onclick = new BtnCallback<C>(obj,func);
  }

  ~Button(){
    delete _onclick;
  }

private:
  BtnCallbackBase* _onclick;
};

class MyClass{
public:
  void ExampleCallback(Button* btn){
    std::cout << "Callback works!\n";
  }
};

int main(){
  Button btn;
  MyClass test;

  btn.RegisterCallback(&test, &MyClass::ExampleCallback);

  btn.Click();
}

Полный примерна Ideone.

1 голос
/ 04 мая 2011

Вы должны использовать function<void(Button*)> объект.Они полиморфны во время выполнения и могут использоваться с любым объектом, который поддерживает void operator()(Button*).Вы можете найти его в Boost, TR1 и C ++ 0x.boost::bind хорошо работает с этими объектами.

0 голосов
/ 06 декабря 2013

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

http://www.gotw.ca/gotw/083.htm

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