2

For example, in the following pseudo code, Class B need to call A::Action() through B::m_cb member.

The objective is, how to make a general, non-template Callback class, so "B" does not have to be a template, and the "CallBack" can hold any kind of function signature.

I ever use such code before, but now I can not find that implementation. All I remember is:
- the "CallBack" itself is not a template, but it contains member template
- the helper function template make_callback will instantiate CallBack object

Can anyone give a 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();
}

Here is the sample code I got from this same website. I tried to improved it a little bit, so it can tolerate arbitrary call back function's return type. But it still can not handle arbitrary number of arguments, like in line 18. Also, , T is the pointer to member function, which should be depend on C. I don't know how to enforce this.

#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();
}
John Crane
  • 371
  • 5
  • 14
  • 1
    Tip: [Boost.Bind](http://www.boost.org/doc/libs/1_47_0/libs/bind/bind.html) already offers this kind of functionality so it spares you having to implement it yourself. – Paul Manta Jul 27 '11 at 18:30
  • Can Boost.Bind handle arbitrary callback function signature? like arbitrary types, and arbitrary number of arguments? – John Crane Jul 27 '11 at 19:15

3 Answers3

2

What you're looking for is std::function (C++0x) /boost::function. These use type-erasure to make functions act like first-class objects. You can do something like this:

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();
}
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Thanks for the response. I've just found a sample code can do this in standard c++. What I still can not do is to deal with arbitrary function signature. Seems that will require to pass "..." into another function: like this: void f(...){ g(...); // pass f()'s args into g(). Any idea how can I do that? – John Crane Jul 27 '11 at 19:10
  • 1
    @John: I don't understand your question. You need to narrow down what you're trying to do and come up with a real use case, not keep asking hypotheticals. – GManNickG Jul 27 '11 at 19:41
  • @John Crane: The signature must be fixed at compile-time. Even with variadic templates, you can still only vary them at compile-time. C++ will not support run-time variant arguments- they must be statically typed. – Puppy Jul 29 '11 at 18:53
2

I have implemented a callback mechanism based on this article:

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

With that implementation you can either use global functions, static member functions or non-static member functions as callbacks.

The article describes how to create delegate objects as functors for all of these types. Your class B could have a delegate object as member and a method for registering a callback function and a method for calling it. Neither class B nor your callback functions have to be templated! The delegate objects contain an object pointer in case a non-static member function is used as callback and a pointer to a templated static wrapper function. In this wrapper function the function pointer to your callback function is stored. It is either invoked directly or before bound to the passed object pointer.

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

  private:
    static CDelegate m_Delegate;
};

Update:

The mechanism is also described here: 5 years later, is there something better than the "Fastest Possible C++ Delegates"?

Community
  • 1
  • 1
Mirco
  • 107
  • 2
  • 11
1

This doesn't really answer your question, because a callback has to be a template, unless you want to pass a parameter through void* (which is totally crazy idea in my opinion).

I asked a similar question : What is wrong with this variadic templates example?

One of answers gave a complete solution :

#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 );
}

Hope it helps. If you hate templates, you can always use a typedef.

Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Thanks. I found another sample code, which is similar, but I believe is better. The public API is template free. What I am still missing is how to handle variable length argument list of the callback function. I'll post that sample code as another answer to this question. Please check – John Crane Jul 27 '11 at 19:13
  • I can not answer my own question within 8 hours. I've pasted the code to the end of my question. Please check. – John Crane Jul 27 '11 at 19:21