1 апр. 2009 г.

Использование boost::function

Во многих современных языках существует понятие «делегата» - некоторой сущности, позволяющей косвенно вызвать тот или иной метод. Наиболее близкая аналогия в С++ - это указатель на функцию или член класса. Но каждый разработчик на С++, кто сталкивался с такого рода указателями, в итоге выясняет, что они - не полноценный аналог делегата. По двум причинам:

Указатель на функцию и указатель на член класса не могут быть преобразованы друг в друга.
Для доступа к члену класса по указателю обязательно требуется указатель на объект, относительно которого производится вызов.

А по этому, если указатели на свободные функции или статические функции еще можно как-то рассматривать в качестве делегатов, то с указателями на члены класса все гозаздо сложнее. Для устранения этих проблем и был создан шаблонный класс boost::function (а точнее - набор шаблонных классов).

Что это такое? По сути - boost::function (точнее, результат его инстанцирования) - это функтор (функциональный объект), к которому применим оператор »()» (вызова функции). А потому в исходный тексте программы вызов метода напрямую или через boost::function ничем не отличимы.

В качестве аргументов шаблона boost::function принимает сигнатуру метода, который он будет «эмулировать». Например:

boost::function<void ()> foo1;
// (соответствует методу с сигнатурой void foo1();)

boost::function<int (int, float, SomeClass&)> foo2;
// соответствует методу
// int foo2(int arg1, float arg2, SomeClass& arg3)

// и т. д.

А инициализироваться может либо указателем на соответствующий метод, либо другим функциональным объектом, который может быть вызван с указанными параметрами:

void foo1(int a) {;}

struct Foo1
{
   void operator() (int a) {;}
}

boost::function<void (int)> Delegate;

//...
Delegate = foo1;
// ...
Delegate(10); // будет реально вызван foo1(10)
// ...
// ...
Delegate = Foo1();
// ...
Delegate(10); // будет вызван Foo1::operator()(10);

Как это использовать

boost::function можно использовать:

Со свободными (глобальными) функциями.
С функциями-членами.
С функциональными объектами.

Синтаксис объявления экземпляра boost::function не меняется. Аргументы, которые может принимать boost::function такие же, какие может принимать обычная функция. При этом (как и для обычной функции) можно перечислять как просто типы аргументов, так и типы с формальными именами параметров:

// оба объявления равноценны
boost::function<void (int, float[], SomeClass&)> f1;
boost::function<void (int a, float arr[], SomeClass& sc)> f2;

Максимальное количество аргументов, которое может принимать boost::function зависит от реализации. Чаще всего - не больше 10.

После объявления экземпляра boost::function его необходимо обязательно заполнить (проинициализировать). До этого момента экземпляр будет считаться «пустым» и при попытки применить к нему оператор вызова функции будет выброшено исключение bad_function_call.

Использование вместе со свободными функциями

Для использования boost::function совместно со свободными функциями достаточно проинициализировать объект адресом соответствующей функции:

void foo(int, int) {;}

boost::function<void (int, int)> f1;
f1 = foo;
// или
f1 = &foo;

Использование вместе с функциями-членами

Используется также, как и со свободными функциями:

class SomeClass
{
public:
 void foo(int, int) {;}
}

boost::function<void (int, int)> f1;
f1 = &SomeClass::foo;

Но использование такого функционального объекта требует передачи ему экземпляра класса, для которого должна быть вызвана функция:

SomeClass obj;
f1(&obj, 10);

В случае необходимости передачи «чистого» функционального объекта ситуацию спасает использование boost::bind, или std::bind1st:

SomeClass* obj = ...;
f1 = boost::bind(&SomeClass::foo, obj, _1);
//...
f1(10); // эквивалентен вызову f1 из предыдущего примера

Статические функции члены передаются в boost::function также, как и свободные функции.

Использование с функциональными объектами

Если для какого-то объекта можно применить оператор вызова функции, то он также, как и обычные функции, может быть использован для инициализации boost::function. Пример был приведен в предыдущем разделе, когда boost::function инициализировался функциональным объектом boost::bind.

Поскольку функциональный объект в boost::function хранится по значению, то в случае необходимости передачи ссылки необходимо воспользоваться boost::ref:

SomeFunctionObject foo;
boost::function<void (int)> f1;
f1 = boost::ref(foo);
boost::function<void (int)> f2;
f2(f1);

В этом случае f1 и f2 будут использовать для работы один и тот же функциональный объект.

P.S.: Наглым образом спёрто отсюда: [[doc:cpp:boost:function]]

Комментариев нет:

Отправить комментарий