Каковы преимущества использования Boost.Phoenix? - PullRequest
15 голосов
/ 16 февраля 2011

Не могу понять, в чем реальные преимущества использования Boost.Phoenix.

Когда я использую его с грамматикой Boost.Spirit, это действительно полезно:

double_[ boost::phoenix::push_back( boost::phoenix::ref( v ), _1 ) ]

Когда я использую его для лямбда-функций, это также полезно и элегантно:

boost::range::for_each( my_string, if_ ( '\\' == arg1 ) [ arg1 = '/' ] );

Но каковы преимущества всего остального в этой библиотеке?В документации написано: «Функторы повсюду».Я не понимаю, что в этом хорошего?

Ответы [ 5 ]

12 голосов
/ 17 февраля 2011

Я укажу вам, в чем заключается критическая разница между Boost.Lambda и Boost.Phoenix:

Boost.Phoenix поддерживает (статически) полиморфные функторы, тогда как привязки Boost.Lambda всегда мономорфны.

(В то же время во многих аспектах две библиотеки могут быть объединены, поэтому они не являются исключительными вариантами выбора.)

Позвольте мне проиллюстрировать (Предупреждение: код не проверен.) :

Phoenix

В Фениксе функтор может превращаться в «ленивую функцию» Феникса (из http://www.boost.org/doc/libs/1_54_0/libs/phoenix/doc/html/phoenix/starter_kit/lazy_functions.html)

struct is_odd_impl{
    typedef bool result_type; // less necessary in C++11
    template <typename Arg>
    bool operator()(Arg arg1) const{
        return arg1 % 2 == 1;
    }
};

boost::phoenix::function<is_odd_impl> is_odd;

is_odd действительно полиморфен (как функтор is_odd_impl). То есть is_odd(_1) может действовать на что угодно (что имеет смысл). Например, в is_odd(_1)(2u)==true и is_odd(_1)(2l)==true. is_odd может быть объединено в более сложное выражение без потери его полиморфного поведения.

Попытка лямбды

Что ближе всего мы можем получить к этому в Boost.Lambda ?, мы могли бы определить две перегрузки:

bool is_odd_overload(unsigned arg1){return arg1 % 2 == 1;}
bool is_odd_overload(long     arg1){return arg1 % 2 == 1;}

но для создания лямбда-функции «ленивый» нам нужно будет выбрать один из двух:

using boost::lambda::bind;
auto f0 = bind(&is_odd_overload, _1); // not ok, cannot resolve what of the two.
auto f1 = bind(static_cast<bool(*)(unsigned)>(&is_odd_overload), _1); //ok, but choice has been made
auto f2 = bind(static_cast<bool(*)(long)>(&is_odd_overload), _1); //ok, but choice has been made

Даже если мы определим версию шаблона

template<class T>
bool is_odd_template(T arg1){return arg1 % 2 == 1;}

нам придется привязать к конкретному экземпляру функции шаблона, например

auto f3 = bind(&is_odd_template<unsigned>, _1); // not tested

Ни f1, ни f2, ни f3 не являются по-настоящему полиморфными, поскольку выбор был сделан во время связывания.

(Примечание 1: это, возможно, не лучший пример, так как может показаться, что все работает из-за неявных преобразований из unsigned в long, но это другой вопрос.)

Подводя итог, можно сказать, что при заданной полиморфной функции / функторе лямбда не может связываться с полиморфной функцией (насколько я знаю), в то время как Феникс может. Это правда, что Phoenix опирается на «Result Of Protocol» http://www.boost.org/doc/libs/1_54_0/libs/utility/utility.htm#result_of, но 1) по крайней мере, это возможно, 2) Это меньше проблем в C ++ 11, где очень легко вывести типы возврата и это можно сделать автоматически.

На самом деле, в C ++ 11 лямбды Phoenix все еще более мощные, чем C ++ 11 встроенные лямбды. Даже в C ++ 14, где template общие лямбды реализован, Феникс еще более общий, потому что он позволяет определенный уровень самоанализа. (Для этого другое, Джоэл де Гусман (разработчик Феникса) был и остается далеко впереди своего время.)

5 голосов
/ 16 февраля 2011

Ну, это очень мощный лямбда-язык.

Я использовал его для создания прототипа математического DSL:

http://code.google.com/p/asadchev/source/browse/trunk/work/cxx/interval.hpp

и многое другое:

http://code.google.com/p/asadchev/source/browse/#svn%2Ftrunk%2Fprojects%2Fboost%2Fphoenix

4 голосов
/ 16 февраля 2011

Я никогда не использовал Феникс, но ...

С Документация библиотеки Феникса :

Библиотека Phoenix позволяет использовать методы FP, такие как функции высшего порядка, лямбда-выражения (функции без имен), карри (применение частичных функций) и отложенные вычисления в C ++

С статья в Википедии о функциональном программировании :

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

Итак, Phoenix - это библиотека для включения функционального программирования на C ++.

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

Правильность, потому что без побочных эффектов код, который вы видите, - это все, что происходит в системе. Какой-то другой код не изменит ваше состояние под вами. Вы можете гораздо проще написать код без ошибок в такой среде.

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

3 голосов
/ 28 февраля 2011

Не смотрите на Boost.Phoenix2.

Эволюция лямбда-выражений в boost выглядит так:

Bind -> Lambda, Phoenix2 (как часть Spirit) -> Phoenix3 (как отдельная библиотека, в разработке).

Результатом является одна лямбда-библиотека с поддержкой полиморфных функторов (другие станут устаревшими). ​​

1 голос
/ 16 февраля 2011

Функциональное программирование на C ++. Это трудно объяснить, если вы ранее не использовали язык с надлежащей поддержкой функционального программирования, такой как SML. Я попытался использовать Phoenix и нашел это хорошим, но очень непрактичным в реальных проектах, потому что это значительно увеличивает время компиляции, а сообщения об ошибках ужасны, когда вы делаете что-то не так. Я помню, как получал несколько мегабайт ошибок от GCC, когда играл с Phoenix. Кроме того, отладка глубоко вложенных шаблонов - это PITA. (На самом деле, это также все аргументы против использования большей поддержки.)

...