C++ OO Callback Technique

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Environments: C++, OO, callback, notification, threading, templates

So, you’re writing C++ and are trying to keep it as OO as possible. You wish to have a server object callback a method in your client object. This could be for purpose of threading, notifications, and so forth. How can one do this without being C++ risky and getting messy?

The following technique is type safe, and does not rely on inheritence or static stubs. You just add the server object as one of the client’s data members and init it with the name of the methods to be called.

The mechanism is based on the server exporting a template callback object. The client creates one using its own type and gives it to the server.

I have used a timer as a server in my example. Thus, setting up a timer to call one of my methods would be as simple as this:


/****************************************************************/
CMyClass::CMyClass()
{
// * Create Callback
CMyTimerCallback<CMyClass>* pCallback =
new CMyTimerCallback<CMyClass>;

// * Set up Callback
pCallback->SetClass(this);
pCallback->SetMethodTick( TimerNotify );

// * Create Timer and attatch Callback to it
m_pMyTimer = new CMyTimer;
m_pMyTimer->InitCallback(pCallback);

// * Set up Timer
m_pMyTimer->SetTimer(1234);
}

/****************************************************************/
CMyClass::TimerNotify(int Id)
{
// …
}
/****************************************************************/

The server side implementation is a little complicated to grasp at first, but once you get it, it makes a lot of sense. It uses a little template magic.


/****************************************************************/
class CMyTimer
{
public:
void InitCallback(CMyTimerCallbackBase* pCallback);
void SetTimer(int iInterval);

private:
CMyTimerCallbackBase* m_pCallback;
};

/****************************************************************/
class CMyTimerCallbackBase
{
public:
virtual void MethodTick(int Id) = 0;
// add other methods here

};

/****************************************************************/
template<class C>
class CMyTimerCallback : public CMyTimerCallbackBase
{
public:
CMyTimerCallback() { m_pClass = 0; m_pMethodTick = 0; }

typedef void (C::*MethodTick_t)(int Id);

void SetClass(C* pClass) { m_pClass =
pClass; }
void SetMethodTick(MethodTick_t pMethod) { m_pMethodTick =
pMethod; }
// add other methods here

protected:

void MethodTick(int Id)
{
if(m_pClass && m_pMethodTick)
{
(m_pClass->*m_pMethodTick) (Id);
}
}

private:
C* m_pClass;
MethodTick_t m_pMethodTick;
};

This technique has proven to be quite useful, I’ve used it to recieve data from a comms link, receive notifications, and to run multiple threads within a single client object.

A download of C++ code is provided that can be compiled and stepped to aid your understanding.

Downloads

Download demo code – 3 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read