C++ Callback Demo


This article was contributed by Elmue.

Environment: Pure C++. Runs on Windows, Mac, Linux, and so on.

Introduction

This fully functional example shows how in C++ callbacks can be done in an absolutely flexible way!

This demo is written in Visual Studio 6 but the Callback class is platform independent and also runs under Linux, Mac, and so forth....

Callbacks in C++ are not as simple as in C. Pure C functions are from the type __cdecl. C++ functions are from the type __thiscall. (They differ in the way how they pass arguments on the stack.)

In C++, you have classes and, additionally, instances of classes. Every instance uses its own memory area for storing class variables. The pointer to this area of variables is the "this" pointer. It represents the instance. Every time you call any C++ function, the "this" pointer is passed to the function as an invisible parameter! (M$ Visual Studio 6 uses the processor register ECX to pass the "this" pointer.)

So, in C++ it is not enough to store the address of the function, which you want to call back. You also have to store the "this" pointer!

Using the Callback Class

You can include "Callback.h" into your project. The usage it is very simple because the cCallback class has only two member functions: SetCallback() and Execute(). You can understand the following examples without knowing what is happening inside cCallback.

cMyProject.h:

#include "callback.h"

private:
    // the functions of your project
    void CallbackFox   (int Param);
    void CallbackRabbit(int Param);
    void TestTheCallback(cCallback *pCallbackFunction, int Param);

    // Some instances of the Callback class
    TCallback<cMyProject> i_Callback_1;
    TCallback<cMyProject> i_Callback_2;

cMyProject.cpp:

void cMyProject::CallbackRabbit(int Param)
{
    char Buf[50];
    sprintf(Buf, "Now I'm in Rabbit with Param %d !\n", Param);
    printf(Buf);
}

void cMyProject::CallbackFox(int Param)
{
    char Buf[50];
    sprintf(Buf, "Now I'm in Fox with Param %d !\n", Param);
    printf(Buf);
}

void TestTheCallback(cCallback *pCallbackFunction, int Param)
{
    pCallbackFunction->Execute(Param * Param);
}

void cMyProject::CallbackDemo()
{
    // defining where the callback should jump to
    i_Callback_1.SetCallback(this, &cMyProject::CallbackRabbit);
    i_Callback_2.SetCallback(this, &cMyProject::CallbackFox);
    
    // now you can pass i_Callback like a pointer to a function
    TestTheCallback(&i_Callback_1, 4);
    TestTheCallback(&i_Callback_2, 5);
}

If you call cMyProject::CallbackDemo(), the output will be:

Now I'm in Rabbit with Param 16 !
Now I'm in Fox    with Param 25 !

Callback Re-Definitions

It is also possible to re-define the callback with SetCallback() as often as you like:

void cMyProject::CallbackDemo()
{
    i_Callback_1.SetCallback(this, &cMyProject::CallbackRabbit);
    TestTheCallback(&i_Callback_1, 4);

    i_Callback_1.SetCallback(this, &cMyProject::CallbackFox);
    TestTheCallback(&i_Callback_1, 5);
}

The output would be the same, but i_Callback_2 is not needed anymore.

Callback Arrays

It is also possible to use arrays of callbacks:

cMyProject.h:

private:
    TCallback<cMyProject> i_Callback[10];

cMyProject.cpp:

void TestTheCallback(int Index, int Param)
{
    i_Callback[Index].Execute(Param * Param);
}

void cMyProject::CallbackDemo()
{
    i_Callback[0].SetCallback(this, &cMyProject::CallbackRabbit);
    i_Callback[1].SetCallback(this, &cMyProject::CallbackFox);
    i_Callback[2].SetCallback(.....);
    
    TestTheCallback(0, 4);
    TestTheCallback(1, 5);
}

Callback Arrays, Part 2

In the above example, all callbacks are from cMyProject. In i_Callback you can ONLY store callbacks to the cMyProject class because it is defined as TCallback<cMyProject>.

If you want to store callbacks to different classes in a callback array, you have to create the array from cCallback instead of TCallback:

cMyProject.h:

private:
    cCallback *p_Callback[10];

cMyProject.cpp:

void cMyProject::StoreCallback(cCallback *p_CallbackFunction,
                               int Index)
{
    p_Callback[Index] = p_CallbackFunction;
}

StoreCallback() then can be called by ANY class to set a callback to itself. For example:

cDemo.h:

private:
    TCallback<cDemo> i_MyCallback;

cDemo.cpp:

#include "cMyProject.h"
extern cMyProject i_MyProject;
......
    i_MyCallback.SetCallback(this, &cDemo::MyCallbackFunction);
    
    i_MyProject.StoreCallback(&i_MyCallback, Index);
......

You can even later modify i_MyCallback with SetCallback() without having to call StoreCallback() again!!

In the source code (see the download link at the end of this article) you find a different example, and additionally a demonstration of a global callback, which you need, if you want to be called back by the operating system. (Windows API callbacks always go into the global namespace.)

The Callback Class

Finally, here comes the great cCallback class itself. It consits of only a header file without a corresponding cpp file.

Callback.h:

class cCallback
{
    public:
        virtual void Execute(int Param) const =0;
};


template <class cInstance>
class TCallback : public cCallback
{
    public:
        TCallback()    // constructor
        {
            pFunction = 0;
        }

        typedef void (cInstance::*tFunction)(int Param);
        
        virtual void Execute(int Param) const 
        {
            if (pFunction) (cInst->*pFunction)(Param);
            else printf("ERROR : the callback function has not
                                 been defined !!!!");
        }

        void SetCallback (cInstance  *cInstancePointer, 
                          tFunction   pFunctionPointer)
        {
            cInst     = cInstancePointer;
            pFunction = pFunctionPointer;
        }

    private:
        cInstance  *cInst;
        tFunction  pFunction;
};

This class defines an Execute() function that takes one integer parameter and returns no parameter (void). You can simply adapt it to your needs; for example, a callback that takes five paramaters and returns a bool. (Then, you have to modify three lines: the two lines beginning with "virtual void Execute" and the typedef.)

To completely understand this class, you need advanced C++ knowledge. I will not explain all the details here because this would be too much.

Instead I recommend the very good book:
Author: André Willms
Title: C++ Programming (German title: "C++ Programmierung")
Publisher: Addison Wesley
ISBN 3-8273-1495-X

And from my homepage, you can download free C++ books in the compiled HTML format.

Downloads

Download source - 21 Kb


Comments

  • A good example

    Posted by kora3 on 05/26/2009 04:40am

    A good example for beginners to learn how to implement callbacks.

    Reply
  • A method for using callbacks in a c++ class

    Posted by Legacy on 07/30/2003 12:00am

    Originally posted by: Wayne

    Hello, some good points and methods have been discussed. I’m by no means an expert on callbacks but recently I wanted to use an SNMP callback function and have it be part of a C++ class (without the draw backs of making it static) I have use this same technique before with threads (_beginthreadex). Anyway here is my solution.


    The API I wanted to use was SnmpCreateSession() which can post a message to a window queue or call a callback function. I wanted to use the callback.

    Here is the final API call.

    m_hSession = SnmpCreateSession (NULL, 0, CallBackFunc , NULL);


    The name of my class is SNMPLISTEN witch is derived from a class ThreadObject.
    (this doesn’t matter but it helps clarify the code)

    In another file I declared a global pointer (pointer to the base class)

    //----------------------------------------------------------
    extern ThreadedObject *gpSnmpClass;

    //----------------------------------------------------------

    class SNMPLISTEN : public ThreadedObject
    { ….

    Private:

    //Func that does the real work
    SNMPAPI_STATUS ProcessCallBack(HSNMP_SESSION hSession, HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam, LPVOID lpClientData );

    //func that gets called by OS
    inline static SNMPAPI_STATUS CALLBACK CallBackFunc(HSNMP_SESSION hSession, HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam, LPVOID lpClientData)
    {
    SNMPLISTEN *pObject = (SNMPLISTEN*) gpSnmpClass;
    return pObject->ProcessCallBack(hSession, hWnd, wMsg, wParam, lParam, lpClientData );

    }

    ….}; //end class

    //---------------------------------------------------------

    SNMPLISTEN class constructor

    SNMPLISTEN::SNMPLISTEN(const char *objectTag, bool *startup) : ThreadedObject(objectTag)
    {…

    gpSnmpClass = this;

    …}

    //---------------------------------------------------------

    The function CallBackFunc() gets called by the OS and then uses a pointer to call a member function ( ProcessCallBack() ) that isn’t static and pass along the params. The ProcessCallBack can then access class member vars and functions.


    This probably isn’t the perfect or most elegant solution. I hope it might help some else out.

    Reply
  • But why function pointers?

    Posted by Legacy on 07/29/2003 12:00am

    Originally posted by: Ben

    First, bravo! It's a good example of how to use function pointers in C++.

    Second, I wonder, in this day and age, why we would want to be using such things? Because we can? Sure, if your goal is to discover the beautiful language that C++ is.

    However, I believe there are better ways to implement callbacks that are more robust, flexible and OOP correct. In my experience, callbacks are commonly used for two things: event notification and provide framework classes.

    For event notification, I believe it is best to have the event handler implement an interface ("subclass an abstract class" in C++ talk) and then the generator of events keeps a set of objects interested in notification of event. This is documented in the "Observer pattern" by the GoF.

    For framework classes, you want to offer basic services and let the user define the details. For instance, a set<> class would define a method to perform an action on every item it contains, and through a "callback", you can define what that action is. Again, this is best implemented by defining an abstract class for the callbacks. See the discussion on "function objects" in C++ books.

    When a callback is implemented as a separate object, you can easily configure it, at run time, by passing arguments to its constructors. Try _that_ with function pointers :)

    >Every instance uses its own memory area for storing class variables

    That would be _instance_ (not class) variables. The class variables are stored in an area of memory shared by all instances.

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

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date