Подтвердить что ты не робот

Каковы преимущества использования Boost.Phoenix?

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

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

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

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

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

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

4b9b3361

Ответ 1

Я укажу вам, что является критическим различием между 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 может. Это правда, что Phoenix полагается на "Результат протокола" http://www.boost.org/doc/libs/1_54_0/libs/utility/utility.htm#result_of, но 1) по крайней мере, это возможно, 2) Это меньше проблема в С++ 11, где типы возврата очень легко вывести, и это можно сделать автоматически.

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

Ответ 3

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

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

Bind → Lambda, Phoenix2 (как часть Духа) → Phoenix3 (как отдельная библиотека, находящаяся в разработке).

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

Ответ 4

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

От документы библиотеки Phoenix:

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

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

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

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

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

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

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

Ответ 5

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