Callback Implementation using Static Function, Member Function and Functor in C++

Discussion in 'C++' started by Mridula, Aug 21, 2009.

  1. Mridula

    Mridula New Member

    Joined:
    Mar 5, 2008
    Messages:
    316
    Likes Received:
    19
    Trophy Points:
    0
    Occupation:
    S/w proffessional
    Location:
    Bangalore

    Introduction



    This article explains about Callbacks and implmentation of it using below 3 different methods:

    1. Static Functions
    2. Non-Static or Member Functions
    3. Functor or Function Objects.

    Background



    About CallBack

    It is an executable code, that is passed as an argument to other independent block of code or function to perform the required task. Here, it separates the callee from caller. Caller only needs to know the prototype of that function that would be used as a callback. So callbacks provide a mechanism where-in independently developed objects can be connected together, to meet the requirement.

    It's Usage

    1. Mostly, these callbacks are written in a common/generic library, so the applications can make use of them on needed basis - like "compare function" that acts as a callback and can be used in searching, sorting objects in a list or container.

    2. If you want to terminate the application because of some trap, then to make sure of things that needs to be taken care before terminating the application, for that the application would register itself to a common callback utility that may be called as "cleanup".

    3. Callbacks are also used in event handling in Finite State Machines or FSM. Wherein callback functions are used to perform set of things, on receiving an event and before changing to next state.

    Lets take one problem/requirement to illustrate the implementation of callbacks using the above mentioned 3 different methods

    The requirement is to find the Employee with given empId in the container. So the function for finding the Employee in the container uses the callback function or functor, for comparing the given empId with the empId of all the employee objects stored in the container and send the appropriate result.

    Classes involed are:

    class CallBackEmpInterface - Is a Base class contains empId_m.

    class CallBackEmployee - Is a Employee class derives from CallBackEmpInterface class

    class CallBackEmpContainer - Contains objects of class CallBackEmplyee


    Implementation of Callback using Static or Member function of class object

    Here Static function acts as a callback, whose function pointer is sent as a argument to the application which is going to make use of that specific callback later in it's computation.

    The code


    Code:
    #include<iostream.h>
    
    class CallBackEmpInterface 
    {
        unsigned int empId_m;
    
      public:
    
        CallBackEmpInterface()
        {
          cout<<"Ctor ::CallBackEmpInterface::default\n";
        }
        
        CallBackEmpInterface(unsigned int empId):empId_m(empId)
        {
          cout<<"Ctor ::CallBackEmpInterface\n";
        }
        
        unsigned int 
        empId()
        {
          return (empId_m);
        }
        
        static bool compareEmpId(CallBackEmpInterface* obj1, 
                              CallBackEmpInterface* obj2)
        {
          cout<<"\n\nCalling static function - CallBackEmpInterface::compareEmpId \n";
          
          if(obj1->empId() == obj2->empId())
          {
            return(0); //---
          }
          else if(obj1->empId() < obj2->empId())
          {
            return(-1); //---
          }
          else
          {
            return(1); //---
          }
        }
    };
    
    class CallBackEmployee : public CallBackEmpInterface 
    {
      public:
    
        CallBackEmployee(unsigned int empId):CallBackEmpInterface(empId)
        {
          cout<<"Ctor ::CallBackEmployee\n";
        }
    };
    
    class CallBackEmpContainer
    {
      public:
    
        CallBackEmpContainer(): basePtr_mpp(NULL)
        {
          basePtr_mpp = new (CallBackEmpInterface *)[2];
        }
    
        void insert(const CallBackEmpInterface *obj_p)
        {
          CallBackEmpInterface *tempObj_p;
          tempObj_p = (CallBackEmpInterface *)obj_p;
          
          if(count !=2)
          {
            basePtr_mpp[count] = tempObj_p;
            ++count;
          }
        }
    
        void find(CallBackEmpInterface *critObj_p, bool (*compFun_p)(CallBackEmpInterface* obj1, CallBackEmpInterface* obj2))
        {
          unsigned int i;
    
          for(i=0; i<2; ++i)
          {
            // here calling the calling back function compareEmpId to find the result
            if(0 == (compFun_p(basePtr_mpp[i], critObj_p)))
            {
              cout<<"Found the object with empId as "<<critObj_p->empId()<<"...\n\n" ;
              return;
            }
          }
          cout<<"The container does not have this empId...\n\n";
        }
        
        
      private:
    
        CallBackEmpInterface ** basePtr_mpp;
        static unsigned int count;
    };
    
    unsigned int CallBackEmpContainer::count = 0;
    
    int main()
    {
      cout<<"Implementation of Callback Function using Static Function ...\n";
      cout<<"----------------------------------------------------------------\n\n";
    
      CallBackEmployee *obj1 = new CallBackEmployee(1);
      CallBackEmployee *obj2 = new CallBackEmployee(2);
      
      CallBackEmpContainer cb;
      cb.insert(obj1);
      cb.insert(obj2);
      
      //Create the criterian, to find a object with empId as "1" in the container
      CallBackEmployee *obj3 = new CallBackEmployee(1);
    
      //Find the object of type criterian obj3 in the container and send the results
      cb.find(obj3, CallBackEmpInterface::compareEmpId);
    
      delete (obj1);
      delete (obj2);
      delete (obj3);
    
      return(0);
    }
    
    Output:
    --------------
    
    Implementation of Callback Function using Static Function ...
    ----------------------------------------------------------------------
    
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    
    
    Calling static function - CallBackEmpInterface::compareEmpId 
    Found the object with empId as 1...
    
    Here in this example, the static function compateEmpId is a comparision function, acts as a callback, whose function pointer is sent as an argument to find function. Where-as this find function is used by the main function for searching the Employee with specific empId in the container.

    Implementation of Callback using Non-Static or Member function

    Since pointer to member or non-static functions of a class object need the this pointer as well, it differs from the signature of the ordinary function pointers. But using the below work around, we can still make a callback to a member function.

    A. Write a Static function as a Wrapper to a member function (which acts as a callback) providing the below arguments as -

    1. void pointer - which can be used to cast to the pointer to the object, whose member function can be invoked as a callback.
    2. Required parameters to call back function.
    B. Call the member function using the cast ed pointer, by sending all the required parameters from this static function.

    C. Then send the pointer to this Static Function as a argument to the function or application, where-in the callback function can be used in it's computation.

    The code


    Code:
    #include<iostream.h>
    
    class CallBackEmpInterface 
    {
        unsigned int empId_m;
    
      public:
    
        CallBackEmpInterface()
        {
          cout<<"Ctor ::CallBackEmpInterface::default\n";
        }
        
        CallBackEmpInterface(unsigned int empId):empId_m(empId)
        {
          cout<<"Ctor ::CallBackEmpInterface\n";
        }
        
        unsigned int 
        empId()
        {
          return (empId_m);
        }
        
        bool compareEmpId(CallBackEmpInterface* obj1, 
                          CallBackEmpInterface* obj2)
        {
          cout<<"\n\nCalling member function CallBackEmpInterface::compareEmpId \n";
          
          if(obj1->empId() == obj2->empId())
          {
            return(0); //---
          }
          else if(obj1->empId() < obj2->empId())
          {
            return(-1); //---
          }
          else
          {
            return(1); //---
          }
        }
    
        static bool wrapperToCompareEmpId(void* thisPtr, CallBackEmpInterface* obj1);
    };
    
    bool CallBackEmpInterface::wrapperToCompareEmpId(void * thisPtr, CallBackEmpInterface *obj1)
    {
      cout<<"\n\nCallBackEmpInterface::wrapperToCompareEmpId\n";
      CallBackEmpInterface *myObj = (CallBackEmpInterface *)thisPtr;
      return (myObj->compareEmpId(myObj, obj1));
    }
    
    class CallBackEmployee : public CallBackEmpInterface 
    {
      public:
    
        CallBackEmployee(unsigned int empId):CallBackEmpInterface(empId)
        {
          cout<<"Ctor ::CallBackEmployee\n";
        }
    };
    
    class CallBackEmpContainer
    {
      public:
    
        CallBackEmpContainer(): basePtr_mpp(NULL)
        {
          basePtr_mpp = new (CallBackEmpInterface *)[2];
        }
    
        void insert(const CallBackEmpInterface *obj_p)
        {
          CallBackEmpInterface *tempObj_p;
    
          tempObj_p = (CallBackEmpInterface *)obj_p;
          
          if(count !=2)
          {
            basePtr_mpp[count] = tempObj_p;
            ++count;
          }
        }
    
        void find(CallBackEmpInterface *critObj_p, bool (*wrapperFunc_p)(void *thisPtr, CallBackEmpInterface* obj1))
        {
          unsigned int i;
    
          for(i=0; i<2; ++i)
          {
            // here calling the calling back function compareEmpId to find the result
            if(0 == (wrapperFunc_p(critObj_p, basePtr_mpp[i])))
            {
              cout<<"Found the object with empId as "<<critObj_p->empId()<<"...\n\n" ;
              return;
            }
          }
          cout<<"The container does not have this empId...\n\n";
        }
        
        
      private:
    
        CallBackEmpInterface ** basePtr_mpp;
        static unsigned int count;
    };
    
    unsigned int CallBackEmpContainer::count = 0;
    
    int main()
    {
      cout<<"Implementation of Callback Function using Non-Static or Member Function ...\n";
      cout<<"----------------------------------------------------------------------------\n\n";
      
      CallBackEmployee *obj1 = new CallBackEmployee(1);
      CallBackEmployee *obj2 = new CallBackEmployee(2);
      
      CallBackEmpContainer cb;
      cb.insert(obj1);
      cb.insert(obj2);
      
      //Create the criterian, to find a object with empId as "1" in the container
      CallBackEmployee *obj3 = new CallBackEmployee(1);
    
      //Find the object of type criterian obj3 in the container and send the results
      cb.find(obj3, CallBackEmpInterface::wrapperToCompareEmpId);
    
      delete (obj1);
      delete (obj2);
      delete (obj3);
    
      return(0);
    }
    
    Output
    ------------
    
    Implementation of Callback Function using Non-Static or Member Function ...
    ------------------------------------------------------------------------------------------
    
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    
    
    CallBackEmpInterface::wrapperToCompareEmpId
    
    
    Calling member function CallBackEmpInterface::compareEmpId 
    Found the object with empId as 1...
    
    Here in this example, compareEmpId is a member function of CallBackEmpInterface class, acts as a callback. This is done by having a static function called wrapperToCompareEmpId in the same class (which takes void pointer and required arguments to the callback function and in turn calls the compareEmpId function in it's implementation) and whose function pointer is sent to the find method and is used in the main function to find Employee with specific empId.

    Implementation of Callback using Function Object or Functor

    Functor is an object that acts like a function. Functor object overloads the parenthesis operator i.e. operator( ) that can return anything and can accept any number of parameters.

    For Example: Here is a Functor that overloads operator ( ) to print a given string.

    Code:
    class MyFunctor
    {
      void operator() (const string& str) const
      {
        cout<<str<<endl;
      }
    }
    
    The same way, we can overload operator() for comparison function and that can be called as a callback from an application.

    The code


    Code:
    #include<iostream.h>
    
    class CallBackEmpInterface 
    {
        unsigned int empId_m;
    
      public:
    
        CallBackEmpInterface()
        {
          cout<<"Ctor ::CallBackEmpInterface::default\n";
        }
        
        CallBackEmpInterface(unsigned int empId):empId_m(empId)
        {
          cout<<"Ctor ::CallBackEmpInterface\n";
        }
        
        unsigned int 
        empId()
        {
          return (empId_m);
        }
        
        virtual bool callback(CallBackEmpInterface* obj1, 
                              CallBackEmpInterface* obj2)
        {
          cout<<"CallBackEmpInterface::callBack \n";
        }
    };
    
    class CallBackEmployee : public CallBackEmpInterface 
    {
      public:
    
        CallBackEmployee(unsigned int empId):CallBackEmpInterface(empId)
        {
          cout<<"Ctor ::CallBackEmployee\n";
        }
    
        bool callback(CallBackEmpInterface* obj1, CallBackEmpInterface* obj2)
        {
          cout<<"\n\nCallBackEmployee::callBack \n";
          
          if(obj1->empId() == obj2->empId())
          {
            return(0); //---
          }
          else if(obj1->empId() < obj2->empId())
          {
            return(-1); //---
          }
          else
          {
            return(1); //---
          }
        }
    };
    
    class CallBackEmpContainer
    {
      public:
    
        CallBackEmpContainer(): basePtr_mpp(NULL)
        {
          basePtr_mpp = new (CallBackEmpInterface *)[2];
        }
    
        void insert(const CallBackEmpInterface *obj_p)
        {
          CallBackEmpInterface *tempObj_p;
          tempObj_p = (CallBackEmpInterface *)obj_p;
          
          if(count !=2)
          {
            basePtr_mpp[count] = tempObj_p;
            ++count;
          }
        }
        
        bool operator () (CallBackEmpInterface* obj1, CallBackEmpInterface* obj2)
        {
          cout<<"\nCalling CallBackEmpInterface::operator () ...\n";
          return((*basePtr_mpp)->callback(obj1, obj2));
        }
    
      private:
    
        CallBackEmpInterface ** basePtr_mpp;
        static unsigned int count;
    };
    
    unsigned int CallBackEmpContainer::count = 0;
    
    int main()
    {
      cout<<"Implementation of Callback Function using Functor or FunctionObject ...\n";
      cout<<"------------------------------------------------------------------------\n\n";
    
      CallBackEmployee *obj1 = new CallBackEmployee(1);
      CallBackEmployee *obj2 = new CallBackEmployee(2);
      
      CallBackEmpContainer cb;
      cb.insert(obj1);
      cb.insert(obj2);
      
      //To find a object with empId as "1" in the container
      CallBackEmployee *obj3 = new CallBackEmployee(1);
    
      //compare objects empId
      if (0 == cb(obj1, obj3))
      {
        cout<<"Found the object with empId as "<<obj3->empId()<<"...\n\n" ;
      }
      else
      {
        cout<<"The container does not have this empId...\n\n";
      }
    
      delete (obj1);
      delete (obj2);
      delete (obj3);
    
      return(0);
    }
    
    Output
    ---------
    
    Implementation of Callback Function using Functor or FunctionObject ...
    -------------------------------------------------------------------------------------
    
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    Ctor ::CallBackEmpInterface
    Ctor ::CallBackEmployee
    
    Calling CallBackEmpInterface::operator () ...
    
    
    CallBackEmployee::callBack 
    Found the object with empId as 1...
    
    Here in this example, CallBackEmpContainer acts as a Functor, having the implementation for operator( ) with 2 arguments for comparison. This acts as a callback and is used in the main function to find the Employee with specific empId. This is done by creating a temporary criterian object of Employee with that specific empId (that needs to be found) and sent as a argument to operator ( ) function.

    Hope this article helps in understanding the Callbacks and it's implementation.

    thanks
    Mridula.

    References



    To know more about Functor or Function Object refer:

    Prefer Function Objects over Function Pointers
    http://www.go4expert.com/showthread.php?t=16504
     
  2. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice