C++ OO Callback Technique

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


Comments

  • Better using a functor

    Posted by Legacy on 11/06/2003 12:00am

    Originally posted by: Robert J. Liu

    It can be better handled using a functor - a function object.
    
    class CMyTimer
    {
    public:
    void MethodTick( int Id, Callback & timerNotify){ timerNotify(Id); }
    void SetTimer(int iInterval);
    ...

    };
    class Callback
    {
    public:
    int operator() (int Id) { ...;}
    };

    int main ()
    {
    Callback timerNotify;
    CMyTimer theTimer;
    theTimer.SetTimer(1234);
    theTimer.MethodTick(Id, timerNotify);
    ...
    }

    Reply
  • try using boost.function and boost.bind

    Posted by Legacy on 11/06/2003 12:00am

    Originally posted by: Shelby

    You could also look at using boost.function (see boost.org for more information)
    
    

    Basically you could do this:

    // create a callback to TimerNotify
    boost::function< void (int) > pCallback =
    boost::bind( CMyClass::TimerNotify, this );

    You would then make your CMyTimer function accept the same type "boost::function< void (int) >" have it call this at every tick.
    No need for any base classes such as "CMyTimerCallbackBase"

    To call this function:

    void CMyTimer::OnTick()
    {
    // call the stored callback
    m_pCallback();
    }

    I recommended check out www.boost.org. It has many other useful libraries worth checking out.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds