Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Phoenix

Boost , ,

Phoenix

В предыдущей главе Феникс был представлен как средство осуществления семантических действий. Мы рассмотрим эту важную библиотеку с акцентом на то, как вы можете использовать ее с помощью Духа. Эта глава ни в коем случае не является исчерпывающим описанием библиотеки. Для получения дополнительной информации о Фениксе, пожалуйста, уделите некоторое время, чтобы прочитать Руководство пользователя Феникса. Если вы просто хотите использовать его быстро, этой главы, вероятно, будет достаточно. Вместо того, чтобы брать вас с теориями и деталями библиотеки, мы постараемся предоставить вам аннотированные примеры. Надеюсь, это быстро приведет вас к высокой скорости.

Семантические действия в Духе могут быть практически любой функцией или функциональным объектом (функтором), если они удовлетворяют требуемой подписи. Например,uint_pтребуется подписьvoid F(T), гдеTявляется типом целого числа (обычнонеподписанный int). Обычные действия ванили являютсяvoid F (IterT, IterT)Разнообразие. Вы можете кодировать свои действия на простом C++. Призывы к функциям или функторам C++ будут иметь формуP[&F]илиP[F()].и т. д. (см.Семантические действия). Phoenix, с другой стороны, пытается имитировать C++ таким образом, чтобы можно было определить функцию тела, вписанную в код.

C++ in C++?

In as much as Spirit attempts to mimic EBNF in C++, Phoenix attempts to mimic C++ in C++!!!

var

Помнишьповышение:? Мы обсуждали это в главеПараметрические Парсеры. Феникс имеет аналогичный, но более гибкий аналог. Он называетсяvar.,и вы можете использовать его в качестве прямой замены. Однако, в отличие отboost::ref, его можно использовать для формирования более сложных выражений. Вот несколько примеров:

    var(x) += 3
    var(x) = var(y) + var(z)
    var(x) = var(y) + (3 * var(z))
    var(x) = var(y)[var(i)] // assuming y is indexable and i is an index

Начнем с простого примера. Мы хотим разобрать запятую, разделив список чисел, и сообщить сумму всех чисел. Используя вар Феникса, нам не нужно писать внешние семантические действия. Мы просто вводим код внутри семантических слотов действий. Вот полная грамматика с нашими действиями Феникса (см.sum.cppв примерах):

    real_p[var(n) = arg1] >> *(',' >> real_p[var(n) += arg1]) 

Полный исходный код можно посмотреть здесь. Это часть духовного распределения.

argN

Обратите внимание на выражение:var(n) = arg1. Что такоеarg1и что он там делает?arg1является аргументатором. Вспомните, чтоreal_p(см.Numerics) сообщает пропаренное число своему прилагаемому семантическому действию.arg1является заполнителем для первого аргумента, переданного семантическому действию парсером. При наличии более чем одного аргумента эти аргументы могут быть отнесены к использованиюarg1.argN. Например, общие семантические действия (интерфейс передачи; см.Семантические действия) передаются 2 аргумента: итераторы (первый/последний) к соответствующей части входного потока. Вы можете обратиться кпервымипоследнимчерезarg1иarg2соответственно.

Как и вар, argN также композитный. Вот несколько примеров:

    var(x) += arg1
    var(x) = arg1 + var(z)
    var(x) = arg1 + (3 * arg2)
    var(x) = arg1[arg2] // assuming arg1 is indexable and arg2 is an index

val

Обратите внимание на выражение:3 * arg2.Это выражение на самом деле является кратким эквивалентом:val(3) * arg2. Позже мы увидим, почему в некоторых случаях нам нужно явно обернуть константы и буквы внутри вала. Опять же, как и var и argN, val также является композитным.

Functions

Помните наш первый пример? В главеQuick Startмы представили парсер, который анализирует список, разделенный запятой, и вставляет парсированные числа в вектор (см.number_list.cpp). Для простоты мы использовали заранее определенных актеров Духа (см.Предопределенные актеры). В примере мы использовалиpush_back_a:

    real_p[push_back_a(v)] >> *(',' >> real_p[push_back_a(v)])

Phoenix позволяет легко писать более мощные полиморфные функции, подобныеpush_back_a.stuff_vector.cpp. Пример похож наnumber_list.cppв функциональности, но на этот раз, используя функцию феникса для фактической реализации функцииpush_back:

    struct push_back_impl
    {
        template <typename Container, typename Item>
        struct result
        {
            typedef void type;
        };
        template <typename Container, typename Item>
        void operator()(Container& c, Item const& item) const
        {
            c.push_back(item);
        }
    };
    function<push_back_impl> const push_back = push_back_impl();

Полный исходный код можно посмотреть здесь. Это часть духовного распределения.

Predefined Phoenix Functions

A future version of Phoenix will include an extensive set of predefined functions covering the whole of STL containers, iterators and algorithms. push_back, will be part of this suite.

push_back_implпредставляет собой простую обертку надpush_backэлементной функцией контейнеров STL. Дополнительные строительные леса предназначены для предоставления фениксу дополнительной информации, которая в противном случае не может быть непосредственно выведена.результатретранслирует в феникс тип возврата функтораоператор(), учитывая его типы аргументовКонтейнериПункт. При этом возвратный тип всегда, простопустота.

push_backявляется объектом функции феникса. Это реальный функциональный объект, который мы будем использовать. Красота объектов функции Феникса заключается в том, что фактическое использование поразительно похоже на обычный вызов функции C++. Вот парсер списка чисел, переписанный с помощью нашего объекта функции феникса:

    real_p[push_back(var(v), arg1)] >> *(',' >> real_p[push_back(var(v), arg1)])

И, в отличие от предопределенных актеров, они могут быть составлены. Видишь рисунок? Вот несколько примеров:

    push_back(var(v), arg1 + 2)
    push_back(var(v), var(x) + arg1)
    push_back(var(v)[arg1], arg2) // assuming v is a vector of vectors and arg1 is an index

push_back не имеет возвратного типа. Скажем, например, мы написали еще одну функцию фениксагрех, мы можем использовать ее и в выражениях:

    push_back(var(v), sin(arg1) * 2)

Construct

Иногда мы хотим создать объект. Например, мы можем захотеть создатьstd::stringс учетом первых/последних итераторов. Например, скажите, что вместо этого мы хотим разобрать список идентификаторов. Наша грамматика без действий:

    (+alpha_p) >> *(',' >> (+alpha_p))

construct_— предопределенная функция феникса, которая, как вы догадались, конструирует объект, исходя из переданных аргументов. Использование:

    construct_<T>(arg1, arg2,... argN)

где T - желаемый тип и arg1..argN - аргументы конструктора. Например, мы можем построитьstd::stringиз пары первого / последнего итератора таким образом:

    construct_<std::string>(arg1, arg2)

Теперь мы прикрепляем действия к нашей грамматике:

    (+alpha_p)
    [
        push_back(var(v), construct_<std::string>(arg1, arg2))
    ]
    >>
    *(',' >>
        (+alpha_p)
        [
            push_back(var(v), construct_<std::string>(arg1, arg2))
        ]
    )

Полный исходный код можно посмотреть здесь. Это часть духовного распределения.

Lambda expressions

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

Lambda Expressions?

Lambda expressions are actually unnamed partially applied functions where placeholders (e.g. arg1, arg2) are provided in place of some of the arguments. The reason this is called a lambda expression is that traditionally, such placeholders are written using the Greek letter lambda .

Phoenix использует трюки, не похожие на те, которые используются Spirit для имитации C++, так что вы можете определить функцию тела, вписанную в код. Это странно, но, как уже упоминалось, Феникс на самом деле имитирует C++ в C++, используя шаблоны выражения. Конечно, есть ограничения...

Все компоненты в выражении Феникса должны бытьактером(на языке феникса) так же, как компоненты в Духе должны бытьпарсером. В духе вы можете написать:

    r = ch_p('x') >> 'y';

Но не:

    r = 'x' >> 'y';

По сути,парсер >>charявляется парсером, ноchar >>charявляется char (сдвиг char справа от другого char).

Те же ограничения распространяются и на Феникс. Например:

    int x = 1;
    cout << var(x) << "pizza"

Это хорошо сформированное выражение Феникса, которое лениво оценивается. Но:

    cout << x << "pizza"

Нет. Такие выражения немедленно исполняются. Синтаксис C++ диктует, что по крайней мереодиноперандов должен быть типом актера Феникса. Это касается и сложных выражений. Например:

    cout << var(x) << "pizza" << "man"

Это оценивается как:

    (((cout << var(x)) << "pizza") << "man")

(cout<< var(x))является актером, по крайней мереодиниз операндов является актером Феникса,((cout<< var(x))<< "pizza")также является актером Феникса, и, таким образом, все выражение также является актером.

Иногда безопасно написать:

    cout << var(x) << val("pizza") << val("man")

Просто чтобы ясно показать, с чем мы имеем дело, особенно со сложными выражениями, точно так же, как мы явно обертываем буквальные строки вstr_p("lit")в Духе.

Феникс (и Дух) также имеет дело с унарными операторами. В таких случаях у нас нет выбора. Операнд должен быть актером Феникса (или парсером Духа). Примеры:

Дух:

    *ch_p('z')  // good
    *('z') // bad

Феникс:

    *var(x) // good (lazy)
    *x // bad (immediate)

Кроме того, в Фениксе для того, чтобы задания и индексация были лениво оценены, объект должен быть актером Феникса. Примеры:

    var(x) = 123 // good (lazy)
    x = 123 // bad (immediate)
    var(x)[0] // good (lazy)
    x[0] // bad, immediate
    var(x)[var(i)] // good (lazy)
    x[var(i)] // bad and illegal (x is not an actor)
    var(x[var(i)]) // bad and illegal (x is not an actor)

Wrapping up

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

Следующая глава,Закрытие, мы увидим больше Феникса. Оставайся на связи.



Статья Phoenix раздела может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-05 14:20:34/0.0064699649810791/0