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();
}
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.