cfm
asked on
Callback functions
I'm trying to set up a callback function in a class, but I'm getting errors at compiling time.
The following gets accepted:
class CEngine
{
public:
void SetCallBack(void (*aFunction)(someclass))
{
aCallBackPointer = aFunction;
}
void CallFunction()
{
aCallBackPointer(some_ correct_pa rameter);
}
private:
void (*aCallBackPointer)(somecl ass);
};
The following cause an error:
class CView
{
public:
void Update(someclass aParameter);
void SetCallBack()
{
CEngine eng;
eng.SetCallBack(Update );
}
};
The error gets generated for the "eng.SetCallBack(Update);" line. The error description is: Can not convert parameter 1 from 'void (someclass)' to 'void (__cdecl *)(someclass)'.
Help !!
The following gets accepted:
class CEngine
{
public:
void SetCallBack(void (*aFunction)(someclass))
{
aCallBackPointer = aFunction;
}
void CallFunction()
{
aCallBackPointer(some_
}
private:
void (*aCallBackPointer)(somecl
};
The following cause an error:
class CView
{
public:
void Update(someclass aParameter);
void SetCallBack()
{
CEngine eng;
eng.SetCallBack(Update
}
};
The error gets generated for the "eng.SetCallBack(Update);"
Help !!
BTW: The example is a Windows timer callback, but it also suits your problem...
Feel free to ask if you need more information...
Feel free to ask if you need more information...
ASKER
Static functions don't have access to the class members and I need that.
I also don't want to pass the class pointer to the engine, since the engine must work with any calling class.
Any other suggestions of doing this?
I also don't want to pass the class pointer to the engine, since the engine must work with any calling class.
Any other suggestions of doing this?
> A class' member function can only be a callback when it's
> declared 'static' (as the compiler tries to pass 'this'
> as the 1st parameter otherwise), e.g.
this is only true when you do not have control over how the callback is actually called, in this case where you are control both ends, everything is fine.
The program below shows the sort of thing you can do (I have not added a parameter to the callback, but this is very easy to do, if you want it).
class AbstractCallback {
public:
virtual void operator()() = 0;
};
template <class T>
class SimpleCallback : public AbstractCallback
{
public:
typedef void (T::*CB)();
SimpleCallback(T* instance, CB callback)
: mInstance(instance), mCallback(callback)
{}
virtual ~SimpleCallback() {}
virtual void operator()() {
(mInstance->*mCallback)();
}
protected:
T* mInstance;
CB mCallback;
};
class CEngine
{
public:
void SetCallBack(AbstractCallba ck* cb) {
mCb = cb;
}
void CallFunction() {
(*mCb)();
}
private:
AbstractCallback* mCb;
};
class CView
{
public:
void Update() {
cout << "Hello" << endl; }
void SetCallBack() {
SimpleCallback<CView>* cb = new SimpleCallback<CView>(this , Update);
CEngine eng;
eng.SetCallBack(cb);
eng.CallFunction(); // Force the callback
}
};
void main()
{
CView cv;
cv.SetCallBack();
}
also, the program does not delete the callback pointer, either the caller or the client can do this depending on how you set things up, but the destructor of CEngine or CView should do this.
> declared 'static' (as the compiler tries to pass 'this'
> as the 1st parameter otherwise), e.g.
this is only true when you do not have control over how the callback is actually called, in this case where you are control both ends, everything is fine.
The program below shows the sort of thing you can do (I have not added a parameter to the callback, but this is very easy to do, if you want it).
class AbstractCallback {
public:
virtual void operator()() = 0;
};
template <class T>
class SimpleCallback : public AbstractCallback
{
public:
typedef void (T::*CB)();
SimpleCallback(T* instance, CB callback)
: mInstance(instance), mCallback(callback)
{}
virtual ~SimpleCallback() {}
virtual void operator()() {
(mInstance->*mCallback)();
}
protected:
T* mInstance;
CB mCallback;
};
class CEngine
{
public:
void SetCallBack(AbstractCallba
mCb = cb;
}
void CallFunction() {
(*mCb)();
}
private:
AbstractCallback* mCb;
};
class CView
{
public:
void Update() {
cout << "Hello" << endl; }
void SetCallBack() {
SimpleCallback<CView>* cb = new SimpleCallback<CView>(this
CEngine eng;
eng.SetCallBack(cb);
eng.CallFunction(); // Force the callback
}
};
void main()
{
CView cv;
cv.SetCallBack();
}
also, the program does not delete the callback pointer, either the caller or the client can do this depending on how you set things up, but the destructor of CEngine or CView should do this.
>>Static functions don't have access to the class members
>>and I need that.
That's (partially) correct. However, there are several methods to get around this - could you explain the environment in which you're using the callback (e.g. the declaration of 'SetCallBack()', which is causing the compiler error)?
>>and I need that.
That's (partially) correct. However, there are several methods to get around this - could you explain the environment in which you're using the callback (e.g. the declaration of 'SetCallBack()', which is causing the compiler error)?
ASKER
I'm writing an engine that controls communication and runs asynchronously. The engine must somehow inform other functions if something was received (e.g. inform the view to display the info).
Creating a new message type and posting that to the message queue is not possible. Asking the other classes to poll the engine is possible, but I don't like it. A callback function seemed the way to go.
Some more info. This all happens in a dll, so the declaration of static global variables is out. The use of a static class variable storing the this pointer is possible if you are sure only one instance of the class will be created.
Any other solutions?
Creating a new message type and posting that to the message queue is not possible. Asking the other classes to poll the engine is possible, but I don't like it. A callback function seemed the way to go.
Some more info. This all happens in a dll, so the declaration of static global variables is out. The use of a static class variable storing the this pointer is possible if you are sure only one instance of the class will be created.
Any other solutions?
The solution I have posted should work for you (with no statics involved at all).
In essence the callback function is wrapped in a class, and the use of an abstract base class and a templated derived class ensures that the solution is generic.
In essence the callback function is wrapped in a class, and the use of an abstract base class and a templated derived class ensures that the solution is generic.
ASKER
Thanks jasonclark. Post something as an answer and I'll accept it.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
class CMyClass: public CWhatever
{
//...
static MyClassesTimerCallBack ( HWND, UINT, UINT, DWORD); // parameter signature for a timer callback
}
To use it (in a method of CMyClass):
// set timer callback procedure....
TIMERPROC lpTimerProc = ( TIMERPROC) MyClassesTimerCallBack´;
// create timer
if ( ! ( m_uTimerID = SetTimer ( NULL,
( UINT) this,
TIMER_DELAY,
lpTimerProc
)
To use it outside of CMyClass, you'll have to specify 'CMyClass::MyTimerCallBack
Let me know if you have further questions.