f + b

Feb 02, 2011 09:56

Пожалуй, function + bind вторая, после умных указателей, по полезности и частоте использовании штука в boost. Совсем не удивительно, что это одна из тех вещей, которая из boost перекочует в стандартную библиотеку C++0x.

В чем полезность сладкой парочки f+b?

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

Второе -- возможность по человечески использовать многие алгоритмы из STL. По человечески, это значит без кучи магических предикатов и mem_fn, без отдельных типов функторов и прочей нечести.
Вот так это работает:
std::find_if( first, last, bind( &X::name, _1 ) == "Peter" )
Единственный момент, который нужно помнить -- это полиморфное решение, и чаще всего оно менее производительно, чем решение на основе STL примитивов.
И да, все мы помним, что в C++0x появилась лямба и замыкания.

Третье -- f+b это путь к ленивости, возможность откладывать и "сериализовать" вызовы.

Большим удобством является копируемость объектов function, как я понимаю, реализация несет под капотом некий аналог shared_ptr.

Приведу совсем недавний пример моего удачного использования f+b на практике.
Мы используем Qt. В одной из частей подсистемы есть модель (model) и представление (view), работающие в разных потоках. Ядро нашей подсистемы работает на нашем собственном message loop, ну а работа с UI в Qt обязательно должна происходить в контексте главного потока приложения. Очевидно, что все вызовы от модели к представлению нужно перекидывать как сообщения в message loop Qt путем всем известных сигналов и слотов.
Для целого ряда задач C++ не самый хороший язык (не в последнюю очередь из-за отсутствия нужных динамических свойств и рефлекшена), так вот сигналы и слоты это костыль, с помощью которого пытаются решить одну из проблем языка. Это решение выглядит хорошо в Python (я про PyQt), но в C++ сигналы и слоты выглядят очень некрасиво и громоздко, помимо всего прочего, требуя еще и работу moc для всех заголовочных файлов с классами, использующими эту подпорку.
В общем, множество вызовов от модели к представлению требовали кучу не самого красивого слот-сигнального кода или... одну единственную функцию (основанию на слот-сигнале), которая умеет перекидывать экземпляр boost::function в контекст главного потока приложения (читай -- в контекст UI). И все, все остальные места, использующие слот-сигналы, были переписаны на вызов этой функции с соответствующим bindингом.
Просто и элегантно.

Из недостатков (помимо производительности, о которой я уже писал), отмечу не очень изящный синтаксис, который в большей степени связан с самим языком, чем с библиотекой.
Ну и подкапотная реализация f+b весьма и весьма громоздка. По одному только bind идет почти сотня перегрузок. Увы, в коде для нашей embedded платформы (для которой нет ни boost, ни более менее вменяемого C++ компилятора) мы эту функциональность использовать не можем.

зы. По близким темам -- можно посмотреть Boost.Functional/Factory, это возможность спрятать в function операцию создания объекта.
Конечно же boost::lambda.
Ну и Boost.Phoenix -- альтернативный взгляд на функции, с очень интересно реализованной ленивостью и лябдами.

c++, programming

Previous post Next post
Up