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

Interfacing

Boost , ,

Interfacing

Модульная конструкция Phoenix делает его чрезвычайно расширяемым. Мы видели, что слой за слоем, вся структура построена на прочном фундаменте. Есть только несколько простых хорошо продуманных концепций, которые выложены как кирпичи. В целом рамки предназначены для расширения. Все, что выше композита и примитивов, на самом деле может рассматриваться как расширение структуры. Этот модульный дизайн был унаследован отSpiritinline parser framework.

Расширение не является навязчивым. И всякий раз, когда компонент или модуль расширяется, новое расширение автоматически становится гражданином первого класса и автоматически распознается всеми модулями и компонентами в рамках. Существует множество способов расширения модуля.

1) Написать и развернуть новый примитив:

До сих пор мы представили лишь несколько примитивов 1) аргументов 2) значений и 3) переменных. Для иллюстрации напишем простое примитивное расширение. Назовем его static_int. Она должна быть параметризирована целым числом. Это похоже на статическую версию класса value, но поскольку она статична, данных вообще нет. Целое число закодировано в своем типе. Вот полный класс (образец 5.cpp):

    template <int N>
    struct static_int {
        template <typename TupleT>
        struct result { typedef int type; };
        template <typename TupleT>
        int eval(TupleT const&) const { return N; }
    };

Вот так. Готово! Теперь мы можем использовать это, поскольку это уже полноценный гражданин Феникса из-за соответствия интерфейса. Давайте напишем подходящий генератор, чтобы было проще использовать наш static_int. Помните, что он должен быть обернут как актер, прежде чем его можно будет использовать. Назовем генератор int_const:

    template <int N>
    phoenix::actor<static_int<N> >
    int_const()
    {
        return static_int<N>();
    }

Теперь мы закончили. Давайте используем его:

    cout << (int_const<5>() + int_const<6>())() << endl;

Печатает "11". Есть много вещей, которые вы можете сделать с этой формой расширения. Например, на ум приходят типы данных. Пример:

    lazy_cast<T>(some_lazy_expression)

2) Написать и развернуть новый состав:

Это сложнее, чем наш первый пример (написание примитивного). Тем не менее, как только вы получите основы, написание композита будет почти механическим и скучным (читай: легко). Проверяйте сообщения.hpp. Все ленивые высказывания написаны в терминах композитного интерфейса.

Ладно, давай продолжим. Вспомните, что ленивое утверждение if_ else (и все заявления на этот счет) возвращается в пустоту. Чего не хватает, и, безусловно, будет полезно, так это выражения C/C++ "cond ? a : b". Очень жаль, что C++ не допустил перегрузки. Вздох. В любом случае, вот код (образец 6.cpp):

    template <typename CondT, typename TrueT, typename FalseT>
    struct if_else_composite {
        typedef if_else_composite<CondT, TrueT, FalseT> self_t;
        template <typename TupleT>
        struct result {
            typedef typename higher_rank<
                typename actor_result<TrueT, TupleT>::plain_type,
                typename actor_result<FalseT, TupleT>::plain_type
            >::type type;
        };
        if_else_composite(
            CondT const& cond_, TrueT const& true__, FalseT const& false__)
        :   cond(cond_), true_(true__), false_(false__) {}
        template <typename TupleT>
        typename actor_result<self_t, TupleT>::type
        eval(TupleT const& args) const
        {
            return cond.eval(args) ? true_.eval(args) : false_.eval(args);
        }
        CondT cond; TrueT true_; FalseT false_; //  actors
    };

Хорошо, это довольно много. Давайте переварим это по частям.

    template <typename CondT, typename TrueT, typename FalseT>
    struct if_else_composite {

Это в основном специализированный композит, в котором 3 актера. Он не работает, поскольку подразумевается. 3 субъекта — cond (состояние типа CondT) true_ (истинная ветвь типа TrueT), false_ (ложная ветвь или тип FalseT).

    typedef if_else_composite<CondT, TrueT, FalseT> self_t;

self_t - это типдеф, который объявляет свой собственный тип: " Что это такое?"

    template <typename TupleT>
    struct result {
        typedef typename higher_rank<
            typename actor_result<TrueT, TupleT>::plain_type,
            typename actor_result<FalseT, TupleT>::plain_type
        >::type type;
    };

Мы уже видели результат раньше. Для базовых классов актеров, таких как композиты и примитивы, параметром является TupleT, то есть искаженные аргументы, переданные от актера.

Итак, учитывая некоторые аргументы, каким будет наш тип возвращения? «Ложь» и «Правда» — это то, что помнят актеры? Итак, во-первых, мы должны спросить их "Каковы ваши *простые* (без ссылок) типы возврата? ";

Зная это, наша задача состоит в том, чтобы знать, какой тип имеет более высокий ранг (назовите ранги более высокий_ранг). Зачем нам это делать? Мы подражаем поведению выражения "cond ? a : b". В C/C++ тип этого выражения - тот (a или b) с более высоким рангом. Например, если a - int и b - double, то результат должен быть двойным.

После этого, наконец, у нас есть тип возврата, типизированный по результату::type.

    if_else_composite(
        CondT const& cond_, TrueT const& true__, FalseT const& false__)
    :   cond(cond_), true_(true__), false_(false__) {}

Это наш конструктор. Мы просто вставляем аргументы конструктора в наши переменные.

    template <typename TupleT>
    typename actor_result<self_t, TupleT>::type
    eval(TupleT const& args) const

Вот наша основная функция члена Эвал. Учитывая self_t, наш тип и TupleT, вычет типа возврата почти канонический. Просто спросите у актера результат, он наверняка узнает.

    {
        return cond.eval(args) ? true_.eval(args) : false_.eval(args);
    }

Мы передаем откачанные арги всем нашим актерам: конд, арг и арг соответственно. Обратите внимание, как это выражение отражает версию C/C++ почти до буквы.

Вот и все. Теперь напишем генератор для этого композита:

    template <typename CondT, typename TrueT, typename FalseT>
    actor<if_else_composite<
        typename as_actor<CondT>::type,
        typename as_actor<TrueT>::type,
        typename as_actor<FalseT>::type> >
    if_else_(CondT const& cond, TrueT const& true_, FalseT const& false_)
    {
        typedef if_else_composite<
            typename as_actor<CondT>::type,
            typename as_actor<TrueT>::type,
            typename as_actor<FalseT>::type>
        result;
        return result(
            as_actor<CondT>::convert(cond),
            as_actor<TrueT>::convert(true_),
            as_actor<FalseT>::convert(false_));
    }

Теперь это должно быть тривиально объяснить. Надеюсь. Опять же, давайте переварить этот фрагмент.

    template <typename CondT, typename TrueT, typename FalseT>

Опять же, есть три элемента: условие CondT «cond», истинная ветвь TrueT «true_» и ложная ветвь FalseT «false_».

    actor<if_else_composite<
        typename as_actor<CondT>::type,
        typename as_actor<TrueT>::type,
        typename as_actor<FalseT>::type> >

Это наша цель. Мы хотим создать этого актера. Теперь, учитывая наши аргументы (cond, true_ и false_), мы не совсем уверены, действительно ли они актеры. Что делать, если пользователь передает булеву правду в виде конда? Конечно, это должно быть преобразовано в актера, иначе Феникс станет берзерком и не сможет принять этого инопланетянина.

    as_actor<T>::type

Это именно то, что нам нужно. Этот тип компьютера преобразует из произвольного типа Т в полноценного действующего гражданина.

    if_else_(CondT const& cond, TrueT const& true_, FalseT const& false_)

Таковы аргументы нашего генератора «if_else_».

    typedef if_else_composite<
        typename as_actor<CondT>::type,
        typename as_actor<TrueT>::type,
        typename as_actor<FalseT>::type>
    result;

Как и раньше, это наш целевой тип возвращения, на этот раз снятый с актера. Это нормально, потому что у актера есть конструктор, который принимает объект BaseT: результат в этом случае.

    return result(
        as_actor<CondT>::convert(cond),
        as_actor<TrueT>::convert(true_),
        as_actor<FalseT>::convert(false_));

Наконец, мы создаем и возвращаем результат. Обратите внимание на то, как мы назвали статическую функцию as_actor:: преобразовать статические функции, чтобы сделать преобразование из T в полноценного актера для каждого из аргументов.

Наконец-то. Теперь мы можем использовать наш новый композит и его генератор:

    //  Print all contents of an STL container c and
    //  prefix " is odd" or " is even" appropriately.
    for_each(c.begin(), c.end(),
        cout
            << arg1
            << if_else_(arg1 % 2 == 1, " is odd", " is even")
            << val('\n')
    );

3) Написать преобразователь as_actorдля конкретного типа:

По умолчанию неизвестный тип T преобразуется в актера>. Скажем, мы только что написали специальный примитивный класс my_lazy 1. Всякий раз, когда у нас есть объект типа my_class, мы хотим автоматически преобразовать его в my_lazy_class.

as_actor— преобразователь типа Phoenix. Все объекты, которые нужно преобразовать из неизвестного типа в актера, проходят через этот класс. Специализация as_actorдля моего класса - это именно то, что нам нужно. Например:

    template <>
    struct as_actor<my_class> {
        typedef actor<my_lazy_class> type;
        static type convert(my_class const& x)
        { return my_lazy_class(x); }
    };

Для справки, вот основной интерфейс_actor:

    template <typename T>
    struct as_actor {
        typedef ??? type;
        static type convert(T const& x);
    };

Где??? Тип актора, возвращаемый статической преобразующей функцией. По умолчанию это:

    typedef value<T> type;

4) Написать специализированного перегруженного оператора для конкретного типа:

Рассмотрим обращение с оператором<< std::ostream, таким как cout. Когда мы видим такое выражение, как:

    cout << "Hello World\n"

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

В таких случаях мы можем обеспечить специализированную перегрузку для работы в качестве ленивого оператора в таких выражениях, как "cout<< arg1<< arg2;", где оперативное поведение отклоняется от стандартного оператора:

  1. std::ostream принимается как LHS по ссылке
  2. std::ostream преобразуется в actor>вместо по умолчанию actor>.

Затем мы поставляем специальную перегрузку (см. Special_ops.hpp):

    template <typename BaseT>
    actor<composite<
        shift_l_op,                     //  an operator tag
        variable<std::ostream>,         //  an actor LHS
        actor<BaseT>,                   //  an actor RHS
    > >
    operator<<(
        std::ostream& _0,               //  LHS argument
        actor<BaseT> const& _1)         //  RHS argument
    {
        return actor<composite<
            shift_l_op,                 //  an operator tag
            variable<std::ostream>,     //  an actor LHS
            actor<BaseT>,               //  an actor RHS
        > >(var(_0), _1);               //  construct #em
    }

Обратите внимание, что эталон std::ostream преобразуется в actor>вместо по умолчанию actor>который не подходит в этом случае.

Это еще не завершено. Обратите внимание также, что специализация для бинарных операторов также должна быть написана. 6).

5) Специализировать рангдля конкретного типа или группы типов:

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

Решение: Напишите специализации ранга для наших цифровых типов. Это тривиально и просто:

    template <> struct rank<integer>    { static int const value = 10000; };
    template <> struct rank<floating>   { static int const value = 10020; };
    template <> struct rank<rational>   { static int const value = 10030; };

Теперь, когда существуют операции смешанного типа, такие как a + b, где a — примитивный встроенный int, а b — наш рациональный класс, будет применено правильное продвижение, и результат будет рациональным. Тип с более высоким рангом победит.

6) Специализировать unary_operatorили binary_operatorдля конкретного типа:

Сценарий: У нас есть итератор, не соответствующий STL, называемый my_iterator. К счастью, его оператор ++ работает так, как и ожидалось. К сожалению, при применении оператора dereference *p он возвращает объект типа my_class, но не следует соглашению STL о том, что классы итераторов имеют обозначение typedef.

Решение, напишите специализацию unary_operator для нашего нестандартного класса:

    template <>
    struct unary_operator<dereference_op, my_iterator> {
        typedef my_class result_type;
        static result_type eval(my_iterator const& iter)
        { return *iter; }
    };

Сценарий: У нас есть устаревшая масштабная реализация, которую мы используем для криптографии. Дизайн класса полностью мертв и не подчиняется всем правилам. Например, его оператор + деструктивен и фактически применяет семантику += для эффективности (да, есть такие мертвые мозгом звери!).

Решение: напишите специализацию binary_operator для нашего нестандартного класса:

    template <>
    struct binary_operator<plus_op, bigint, bigint> {
        typedef bigint& result_type;
        static result_type eval(bigint& lhs, bigint const& rhs)
        { return lhs + rhs; }
    };

Возвращаясь к нашему примеру в No 4, мы также должны написать бинарный_операторспециализацию для потоков, потому что оператор<< для потоков отклоняется от нормального поведения.

    template <typename T1>
    struct binary_operator<shift_l_op, std::ostream, T1> {
        typedef std::ostream& result_type;
        static result_type eval(std::ostream& out, T1 const& rhs)
        { return out << rhs; }
    };

7) Просто напишите ленивую функцию.

Подумайте об этом:

    struct if_else_func {
        template <typename CondT, typename TrueT, typename FalseT>
        struct result {
            typedef typename higher_rank<TrueT, FalseT>::type type;
        };
        template <typename CondT, typename TrueT, typename FalseT>
        typename higher_rank<TrueT, FalseT>::type
        operator()(CondT cond, TrueT const& t, FalseT const& f) const
        { return cond ? t : f; }
    };
    function<if_else_func> if_else_;

И это соответствующее использование:

    //  Print all contents of an STL container c and
    //  prefix " is odd" or " is even" appropriately.
    for_each(c.begin(), c.end(),
        cout
            << arg1
            << if_else_(arg1 % 2 == 1, " is odd", " is even")
            << val('\n')
    );

Что такое $%?! Если мы можем сделать это, почему на земле мы пошли на все проблемы, закручивая наш мозг наизнанку с помощью композитного if_else в no. 2? Эй, не так быстро, есть небольшая разница, которая оправдывает композит if_else: Это не очевидно в примере, но составная версия if_else_ оценивает либо истинную, либо ложную ветвь, но не обе **. Приведенная выше версия с ленивой функцией всегда охотно оценивает все свои аргументы до того, как функция будет названа. Таким образом, если мы хотим строго придерживаться семантики C/C++, нам нужна композитная версия.

Кроме того, мне нужно показать пример... Хммм, так в чем тогда смысл No7? Ну, в большинстве случаев ленивой функции будет достаточно. Эти звери довольно могущественны.



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




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



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


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-09-16 13:12:35/0.0099680423736572/0