C ++ Callback - Как отделить тип обратного вызова - PullRequest
2 голосов
/ 27 июля 2011

Например, в следующем псевдокоде класс B должен вызвать A :: Action () через член B :: m_cb.

Цель состоит в том, как сделать общий не шаблонный обратный вызовкласс, поэтому «B» не обязательно должен быть шаблоном, а «CallBack» может содержать любую сигнатуру функции.

Я когда-либо использовал такой код раньше, но сейчас не могу найти эту реализацию.Все, что я помню:
- сам "CallBack" не является шаблоном, но он содержит шаблон
- шаблон вспомогательной функции make_callback будет создавать экземпляр объекта CallBack

Может кто-нибудь датьpoiinter?

Class A
{
public:
   void Action(){//...};
};

class CallBack
{
   //...
   // CallBack it self it is a NOT a template
   // It can wrap member template though
};

class B
{
public:
   void SetCallback(CallBack to){
      m_cb = to;
   }
   void do_something()
   {
      //...
      m_cb.Execute();
      //...
   }

private:
   CallBack m_cb;

};

int main()
{
   A obj1;
   CallBack cb = make_callback(&obj1, &A::Action);
   B obj2;
   obj2.SetCallback(cb);
   //....
   obj2.do_something();
}

Вот пример кода, который я получил с этого же сайта.Я попытался немного улучшить его, чтобы он мог терпеть произвольный тип возврата функции возврата.Но он по-прежнему не может обрабатывать произвольное количество аргументов, как в строке 18. Кроме того, T является указателем на функцию-член, которая должна зависеть от C. Я не знаю, как обеспечить это.

#include <iostream>
#include <memory>

// INTERNAL CLASSES

class CallbackSpecBase
{
  public:
    virtual ~CallbackSpecBase() {}
    virtual void operator()(...) const = 0;
};

template<class C, class T>
class CallbackSpec : public CallbackSpecBase
{
  public:
    CallbackSpec(C& o, T m) : obj(o), method(m) {}
/*line 18*/    void operator()(...) const { (&obj->*method)(); } // how to pass "..." into method(...)

  private:
    C& obj;
    T method;
};

// PUBLIC API

class Callback
{
  public:
    Callback() {}

    void operator()() { (*spec)(); }

    template<class C, class T>
      void set(C& o, T m) { spec.reset(new CallbackSpec<C, T>(o, m)); }

  private:
    std::auto_ptr<CallbackSpecBase> spec;
};

// TEST CODE

class Test
{
  public:
    void foo() { std::cout << "Working" << std::endl; }
    void bar() { std::cout << "Like a charm" << std::endl; }
};

int main()
{
  Test t;
  Callback c;
  c.set(t, &Test::foo);
  c();
  c.set(t, &Test::bar);
  c();
}

Ответы [ 3 ]

2 голосов
/ 27 июля 2011

Что вы ищете, это std::function (C ++ 0x) / boost::function.Они используют стирание типов, чтобы заставить функции действовать как первоклассные объекты.Вы можете сделать что-то вроде этого:

class A
{
public:
    void Action() {//...};
};

class B
{
public:
    template <typename Func>
    void SetCallback(Func func) {
        m_cb = func;
    }

    void do_something() {
        m_cb(); // whatever function
    }

private:
    std::function<void()> m_cb; // anything callable with no arguments
};

int main()
{
    A obj1;
    B obj2;

    obj2.SetCallback(make_callback(&obj1, &A::Action));
    // or:
    obj2.SetCallback(std::bind(&obj1, &A::Action)); // or boost::bind

    obj2.do_something();
}
2 голосов
/ 29 июля 2011

Я реализовал механизм обратного вызова на основе этой статьи:

http://www.codeproject.com/KB/cpp/CPPCallback.aspx

С этой реализацией вы можете использовать глобальные функции , статические функции-члены или нестатические функции-члены в качестве обратных вызовов.

В статье описывается, как создавать объекты-делегаты в качестве функторов для всех этих типов. Ваш class B может иметь делегированный объект в качестве члена и метод для регистрации функции обратного вызова и метод для ее вызова. Ни class B, ни ваши функции обратного вызова не должны быть шаблонными! Объекты делегатов содержат указатель на объект в случае, если в качестве обратного вызова используется нестатическая функция-член, и указатель на шаблонную статическую функцию-обертку. В этой функции-обертке хранится указатель на функцию обратного вызова. Он вызывается напрямую или перед привязкой к переданному указателю объекта.

class B
{
  public:
    void SetCallback(CDelegate f_Delegate)
    {
      m_Delegate = f_Delegate;
    }
    static void do_something()
    {
      //...
      m_Delegate();
      //...
    }

  private:
    static CDelegate m_Delegate;
};

Обновление:

Механизм также описан здесь: 5 лет спустя, есть ли что-то лучше, чем «Самые быстрые делегаты C ++»?

1 голос
/ 27 июля 2011

Это на самом деле не отвечает на ваш вопрос, потому что обратный вызов должен быть шаблоном, если вы не хотите передать параметр через void* (что, на мой взгляд, совершенно безумная идея).

Я задал похожий вопрос: Что не так с этим примером шаблонов с переменным числом аргументов?

Один из ответов дал полное решение:

#include <memory>

template< typename R, typename ... Args >
class CallbackBase
{
public:
    typedef std::shared_ptr< CallbackBase< R, Args... > >
            CallbackPtr;

    virtual ~CallbackBase()
    {
    }
    virtual R Call(  Args ... args) = 0;
};

template< typename R, typename ... Args >
class FunctionCallback : public CallbackBase< R, Args... >
{
public:
    typedef R (*funccb)(Args...);

    FunctionCallback( funccb  cb_ ) : 
        CallbackBase< R, Args... >(),
        cb( cb_ )
    {
    }
    virtual ~FunctionCallback()
    {
    }
    virtual R Call(Args... args)
    {
      return cb( args... );
    }
private:
  funccb cb;
};

template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackPtr
    MakeCallback( R (*cb)(Args...)  )
{
    typename CallbackBase< R, Args... >::CallbackPtr
        p( new FunctionCallback< R, Args... >( cb )
);
    return p;
}

bool Foo_1args( const int & t)
{
    return true;
}
int main()
{
    auto cbObj = MakeCallback( & Foo_1args );
}

Надеюсь, это поможет. Если вы ненавидите шаблоны, вы всегда можете использовать typedef.

...