. . . and boost::lambda

Author / Автор: Sergey Satskiy
Publication date / Опубликовано: 26.09.2005
Version / Версия текста: 1.0

Introduction

A model of callback functions registration in relation to some entity and their further calls at the right moment suits well for some applications. The model can be depicted as follows:


Figure 1. Model of callback functions registration and their further calls

Supposed sequence of actions for the model is as follows:

  • Callback functions are registered. There could be many functions.
  • An external for the entity event appears.
  • All the registered callback functions are called consequently and all the required parameters are passed.

Implementation of that model is not dificult.

Simplifications

Let us make some simplifications for the further discussion. Let us take a plain double variable as an entity which considers assignment of a new value as an external event.

Callback function has one argument - reference to a double variable.

void f( const double &  Value )
{
    cout << "f called. Value: " << Value << endl;
}

Let us implement the entuty as a class with an interface like this:

struct CallbackStorage
{
    CallbackStorage()    { . . . }
    void insert( . . . ) { . . . }
    void operator() ( const double &  val ) { . . . }
};

It is necessary to note that all the code given here is a scetch only which should not be used in industrial software without considerable changes. For example the structure is used only to minor reduction of code typing.

The std::vector of boost::function objects suites well to store callback functions.

    std::vector< boost::function< void ( const double & ) > >       v;

Implementations of the member function insert(. . .) which provides a callback registration is trivial.

    void insert( function< void ( const double & ) >  func )
    {
        v.push_back( func );
    }

The member function operator() provides external event processing i.e. calls all the registered callback functions and passes them the new value.

    void operator() ( const double &  val )
    {
        vector< function< void ( const double & ) > >::iterator k;
        for ( k = v.begin(); k != v.end(); ++k )
        {
            (*k)( val );
        }
    }

The constructor is empty.

The Most Interesting

Now let us suppose that the similar code is used in an application which uses many double variables and allows registering / deregistering callback functions separately for each of them. Sometimes it is very convenient not to use an explicitly written callback function but to have an up to date value always "at hand". That is to use the following logic - to pass as a registration not a function but a reference to a variable which is supposed to be updated as soon as an entity is updated.

In other words the following overloaded member function insert() is required.

    void insert( double &  Ref ) { . . . }

What to do with the given reference? The brutal force option is to have another list in the CallbackStorage and to have another cycle in the operator() which makes assignments. This option does not seem to be very elegant.

The boost::lambda library allows to avoid having the second container as well as the second cycle. The overloaded member function insert() can be implemented as follows:

    void insert( double &  Ref )
    {
        function< void ( const double & ) >  g = ( var( Ref ) = _1 );
        v.push_back( g );
    }

It is very laconically!

Example

The corresponding source code can be found at the author's web site: http://satsky.spb.ru.

References

  1. The boost library documentation (http://www.boost.org).


Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

Разрешается копирование и распространение этой статьи любым способом без внесения изменений, при условии, что это разрешение сохраняется.
Last Updated: September 26, 2005