Вместе с boost::lambda

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

Введение

Для решения некоторых задач хорошо подходит модель регистрации callback функций в связи с какой-нибудь сущностью и последующий их вызов в нужный момент. На рисунке такое взаимодействие можно показать следующим образом.


Рисунок 1. Модель регистрации и последующего вызова callback функций

Предполагаемая последовательность событий для такой модели следующая:

  • Регистрируются callback функции. Таких функций может быть несколько.
  • Появляется какое-либо внешнее для сущности событие.
  • Производится последовательный вызов всех зарегистрированных callback функций с передачей нужных параметров.

Реализация подобной модели не представляет трудности.

Упрощения

Для дальнейших рассуждений сделаем несколько упрощений. Будем считать, что в качестве сущности имеется double переменная, внешним событием для которой будет присвоение ей нового значения.

Callback функция имеет один аргумент - ссылка на double переменную.

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

Сущность реализуем в виде класса с примерно таким интерфейсом:

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

Стоит заметить, что весь приводимый здесь код - только набросок, который не следут использовать в промышленном программном обеспечении без существенных изменений. Так, например, структура использована только для несущественного сокращения записи.

Для хранения списка callback функций отлично подойдет std::vector объектов boost::function.

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

Реализация члена-функции insert(. . .), обеспечивающего регистрацию callback функции, тривиальна.

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

Функция-член operator() обеспечит обработку внешнего события, то есть вызов всех зарегистрированных функций с передачей им нового значения.

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

Конструктор оставим пустым.

Самое интересное

Теперь предположим, что подобный код используется в приложении, которое использует множество double переменных и позволяет раздельно регистрировать / дерегистрировать callback функции для каждой из них. Иногда может оказаться удобным не использовать явно написанную callback функцию, а иметь обновленное значение всегда "под рукой". То есть использовать следующую логику - передавать в качестве регистрации не функцию, а ссылку на double переменную, считая что она будет обновлена, как только будет произведено присвоение.

Другими словами потребуется перегруженный вариант функции-члена insert().

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

Что делать с переданной ссылкой? Лобовой вариант - хранить еще один список внутри CallbackStorage и в operator() добавить второй цикл, в котором и производить присваивания. Однако этот вариант кажется не очень изящным.

Библиотека boost::lambda позволит избежать хранения еще одного списка и дополнительного цикла. Вторую функцию-член insert(. . .) можно реализовать следующим образом.

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

Получилось чрезвычайно лаконично!

Пример

Исходный код примера можно найти на интернет сайте автора: (http://satsky.spb.ru).

Литература

  1. Документация по библиотеке boost (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