Thursday, February 25, 2010

#c++: function callbacks between classes and accessing non-static members from static methods

i was looking for a way to use a class in c++ as a processing container, which does callbacks to methods in another class. an example in digital signal processing can be given as: a tone generator which normally operates at sample rate fs=44100 Hz, but is oversampled to fs * 2 via an oversampling container.

now, of course the above can be inlined in a single method, which will be essentially faster, but leaves us with a composite method, instead of a case where both processes (tone generator and oversampler) are separated in classes and can be reused by other processes in a more object oriented class structure.

lets take a look at a case where the main processing method is in the parent class and the other processing method (a processing container) is in a sub class (or a class which is initiated inside the parent class).

here is an example:

// include a class from a header
#include "childclass.h";
// define parent class
class parentclass
{
  private:
    // child class instance
    childclass _childclass;
    // some variable
    float some_variable;
    
  public:
    // constructor
    parentclass()
    {
      // init stuff
      some_variable = 5.f;
    }
    
    // the main method
    virtual void processing_main_method(const float input)
    {
      // call a method from the child class and pass the input variable
      _childclass.some_method(input);
    }
}


how about callbacks i.e. passing a method which to be called from another method? in the c programming language to achieve that function pointers are used.here is a nice article that covers mostly everything on the subject of function pointers on callbacks:
http://www.newty.de/fpt/callback.html

but what if we decide to make a callback from the child class to a method in the parent class? the logical way to achieve that is to declare a method in the parent class as static. http://www.eelab.usyd.edu.au/ee_database/programming/c++/cplusplus-5.html:

...member of a class can be declared static; be it in the public or private part of the class definition. Such a data member is created and initialized only once; in contrast to non-static data members, which are created for each object of the class. A static data member is created when the program starts executing; but is nevertheless part of the class.

(note: info on static functions in the same article - section 5.2)

some code for our example:

// ==============
// program.cpp
// ==============
// include a class from a header
#include "childclass.h";
// define parent class
class parentclass
{
  private:
    // child class instance
    childclass _childclass;
    // some variable
    float some_variable;
    
  public:
    // constructor
    parentclass()
    {
      // init stuff
      some_variable = 5.f;
    }
    
    // ************************
    // the callback method
    // ************************
    static float processing_callback_method(const float input)
    {
      // do something
    }
    
    // the main method
    virtual void processing_main_method(const float input)
    {
      // call a method from the child class and pass the input variable
      // and also the local processing_callback_method
      _childclass.some_method(processing_callback_method, input);
    }
}

// ===============
// childclass.h
// ===============
// define child class
class childclass
{
  private:    
    float some_variable;
    
  public:
    // constructor
    childclass()
    {
      // init stuff
      some_variable = 5.f;
    }
    
    // ********************************************
    // the method that is called from parent
    // notice how the function pointer is passed
    // ********************************************
    virtual float some_method(float (*callback_function)(float), const float input)
    {
      // do something with input
      // ....
      
      // then do a callback
      return(*callback_function)(input);
    }
}


here is a quick diagram of the above:

.------------------------------------------.
|[*mainclass]                              |
|                                          |
|     processing_main_method               |
|           |                              |
|           |  processing_callback_method  |
|           V                  ^           |
|           |                  |           |
|  .--------|------------------|---.       |
|  |        |                  |   |       |
|  |        '--> some_method ->'   |       |
|  |[*subclass]                    |       |
|  '-------------------------------'       |
'------------------------------------------'


so far so good...
but now we face the issue that static methods cannot access non-static members. actually there is much sense in that if we think about it:

Besides static data, C++ allows the definition of static functions. Similar to the concept of static data, in which these variables are shared by all objects of the class, static functions apply to all objects of the class.

The static functions can therefore address only the static data of a class; non-static data are unavailable to these functions. If non-static data could be addressed, to which object would they belong? Similarly, static functions cannot call non-static functions of the class. All this is caused by the fact that static functions have no this pointer.


the way to solve this would be to pass an instance to the parent class to the subclass function (some_method) and then pass this instance back to the static callback function (processing_callback_method). an example (with void pointers):

// ==============
// program.cpp
// ==============
// include a class from a header
#include "childclass.h";
// define parent class
class parentclass
{
  private:
    // child class instance
    childclass _childclass;
    // some variable
    float some_variable;
    
  public:
    // constructor
    parentclass()
    {
      // init stuff
      some_variable = 5.f;
    }
    
    // *******************************
    // a non-static member function
    // *******************************
    virtual float non_static_method(const float input)
    {
      return input * 2.f;
    }
    
    // ************************
    // the callback method
    // ************************
    static float processing_callback_method(void* _this, const float input)
    {
      // cast the class
      childclass* _pthis = (childclass*) _this;
          
      // do something with a non-static member
      _pthis->some_variable = 1.f;
      
      // call a non-static member function
      return _pthis->non_static_method(input * 0.5f);
    }
    
    // the main method
    virtual void processing_main_method(const float input)
    {
      // call a method from the child class and pass the input variable
      // and also the local processing_callback_method
      _childclass.some_method(processing_callback_method, input);
    }
}

// ===============
// childclass.h
// ===============
// define child class
class childclass
{
  private:
    float some_variable;
    
  public:
    // constructor
    childclass()
    {
      // init stuff
      some_variable = 5.f;
    }
    
    // ********************************************
    // the method that is called from parent
    // notice how the function pointer is passed
    // but also an instance of the parent class
    // ********************************************
    virtual float some_method(void* _parent, float (*callback_function)(void*, float), const float input)
    {
      // do something with input
      // ....
      
      // then do a callback
      return (*callback_function)(_parent, input);
    }
}


so that is all.

please note that i have not tested any of the code from above and its just provided as a guideline.

lubomir

0 comments: