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

The Full Extension Mechanism

Boost , Chapter 1. Fusion 2.2 , Extension

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Библиотека Fusion предназначена для расширения, можно легко добавлять новые типы последовательностей. На самом деле, библиотека поддерживаетstd:Пара,повышение:См.иMPLпоследовательности полностью обеспечиваются с использованием механизма расширения.

Процесс добавления нового типа последовательности в Fusion:

  1. Включите механизм отправки меток, используемый Fusion для вашего типа последовательности
  2. Проектирование типа итератора для последовательности
  3. Обеспечить специализированное поведение для внутренних операций новой последовательности Fusion
Our example

Чтобы проиллюстрировать возможность нового типа последовательности для использования с Fusion, мы будем использовать тип:

namespace example
{
    struct example_struct
    {
        std::string name;
        int age;
        example_struct(
            const std::string& n,
            int a)
            : name(n), age(a)
        {}
    };
}

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

Enabling Tag Dispatching

Механизм расширяемости Fusion используетотправку метокдля вызова правильного кода для данного типа последовательности. Чтобы использовать механизм отправки тегов, мы должны сначала объявить новый тип тегов для использования механизма. Например:

namespace example {
    struct example_sequence_tag; // Only definition needed
}

Далее нам нужно включитьпризнаки::тег_метафункции, чтобы вернуть наш вновь выбранный тип тега для операций, включающих нашу последовательность. Это делается путем специализациичерт::тег_для нашего типа последовательности.

#include <boost/fusion/support/tag_of_fwd.hpp>
#include <boost/fusion/include/tag_of_fwd.hpp>
namespace boost { namespace fusion { namespace traits {
    template<>
    struct tag_of<example_struct>
    {
        typedef example::example_sequence_tag type;
    };
}}}

::tag_ofтакже имеет второй аргумент шаблона, который может использоваться в сочетании сboost::enable_ifдля обеспечения поддержки тегов для групп связанных типов. Эта функция не является необходимой для нашей последовательности, но для примера см. код в:

#include <boost/fusion/adapted/array/tag_of.hpp>
#include <boost/fusion/include/tag_of.hpp>
Designing a suitable iterator

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

Мы будем использовать простую конструкцию, в которой 2 членаexample_structприведены нумерованные индексы, 0 дляимении 1 длявозрастасоответственно.

template<typename Struct, int Pos>
struct example_struct_iterator
    : boost::fusion::iterator_base<example_struct_iterator<Struct, Pos> >
{
    BOOST_STATIC_ASSERT(Pos >=0 && Pos < 3);
    typedef Struct struct_type;
    typedef boost::mpl::int_<Pos> index;
    typedef boost::fusion::random_access_traversal_tag category;
    example_struct_iterator(Struct& str)
        : struct_(str) {}
    Struct& struct_;
};

Краткое изложение деталей нашего итератора:

  1. Итератор параметризируется по типу, который он повторяет, и индексу текущего элемента.
  2. Typedefsstruct_typeиindexобеспечивают удобный доступ к информации, которая нам понадобится позже в реализации.
  3. Категория typedefпозволяетчертам::категории_метафункции для установления категории прохождения итератора.
  4. Конструктор сохраняет ссылку например_структура, который повторяется.

Нам также необходимо включитьотправку теговдля нашего типа итератора с другой специализациейпризнаков::тег_of.

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

A first couple of instructive features

Для начала мы получимрезультат_ из::значение_ изРаботающая метафункция. Для этого мы предоставляем специализацию ускорения::слияния::расширения::значение_of_implшаблон для типа тега нашего итератора.

template<>
struct value_of_impl<example::example_struct_iterator_tag>
{
    template<typename Iterator>
    struct apply;
    template<typename Struct>
    struct apply<example::example_struct_iterator<Struct, 0> >
    {
        typedef std::string type;
    };
    template<typename Struct>
    struct apply<example::example_struct_iterator<Struct, 1> >
    {
        typedef int type;
    };
};

Сама реализация довольно проста, она просто использует 2 частичные специализации для обеспечения типа 2 различных членовexample_struct, на основе индекса итератора.

Чтобы понять, какзначение_of_implиспользуется библиотекой, мы рассмотрим реализациюрезультата_of::значение_of:

template <typename Iterator>
struct value_of
    : extension::value_of_impl<typename detail::tag_of<Iterator>::type>::
        template apply<Iterator>
{};

Такрезультат_::значение_используетотправка метокдля выбораMPL метафункционального классадля обеспечения его функциональности. Вы заметите этот шаблон на протяжении всей реализации Fusion.

Хорошо, давайте включим отсылку к нашему итератору. В этом случае мы должны обеспечить подходящую специализациюderef_impl.

template<>
struct deref_impl<example::example_struct_iterator_tag>
{
    template<typename Iterator>
    struct apply;
    template<typename Struct>
    struct apply<example::example_struct_iterator<Struct, 0> >
    {
        typedef typename mpl::if_<
            is_const<Struct>, std::string const&, std::string&>::type type;
        static type
        call(example::example_struct_iterator<Struct, 0> const& it)
        {
            return it.struct_.name;
        }
    };
    template<typename Struct>
    struct apply<example::example_struct_iterator<Struct, 1> >
    {
        typedef typename mpl::if_<
            is_const<Struct>, int const&, int&>::type type;
        static type
        call(example::example_struct_iterator<Struct, 1> const& it)
        {
                return it.struct_.age;
            }
        };
    };
}

Использованиеderef_implочень похоже на использованиеvalue_of_impl, но оно также обеспечивает некоторую функциональность среды выполнения на этот раз с помощьювызовастатической функции члена. Чтобы увидеть, как используетсяderef_impl, давайте посмотрим на реализациюderef:

namespace result_of
{
    template <typename Iterator>
    struct deref
        : extension::deref_impl<typename detail::tag_of<Iterator>::type>::
            template apply<Iterator>
    {};
}
template <typename Iterator>
typename result_of::deref<Iterator>::type
deref(Iterator const& i)
{
    typedef result_of::deref<Iterator> deref_meta;
    return deref_meta::call(i);
}

Так что опятьрезультат_::отказиспользуетотправку меткиточно так же, какрезультат_::значение_реализация. Функциональность выполнения, используемаяderef, обеспечиваетсявызовомстатической функции выбранногоMPL метафункционального класса.

Реальная реализацияderef_implнемного сложнее, чем реализацияvalue_of_impl. Нам также необходимо реализовать функциювызова, которая возвращает ссылку на соответствующий элемент базовой последовательности. Нам также требуется немного метапрограммирования, чтобы вернутьссылки, если базовая последовательность является const.

[Note] Note

Несмотря на то, что осталось сделать достаточное количество для получения полноценной последовательности Fusion,результат_::значение_иотменаиллюстрируют все существенные требуемые понятия. Остальная часть процесса очень повторяется, просто требуя реализации подходящегоxxxx_implдля каждого признакаxxxx.

Implementing the remaining iterator functionality

Хорошо, теперь мы видели, какрезультат_::значение_иотменаработают, все остальное будет работать почти таким же образом. Начнем с прямой итерации, предоставивnext_impl:

template<>
struct next_impl<example::example_struct_iterator_tag>
{
    template<typename Iterator>
    struct apply
    {
        typedef typename Iterator::struct_type struct_type;
        typedef typename Iterator::index index;
        typedef example::example_struct_iterator<struct_type, index::value + 1> type;
        static type
        call(Iterator const& i)
        {
             return type(i.struct_);
        }
    };
};

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

Мы также должны предоставить подходящийequal_to_impl, чтобы итераторы можно было правильно сравнивать.Двунаправленный итератортакже будет нуждаться в реализацииprior_impl. ДляИтератор случайного доступаdistance_implиadvance_implтакже необходимо предоставить, чтобы удовлетворить необходимые гарантии сложности. Поскольку наш итератор являетсяитератором случайного доступа, нам придется реализовать все эти функции.

Полные реализацииprior_impl,advance_impl,distance_implиequal_to_implприведены в примере кода.

Implementing the intrinsic functions of the sequence

Чтобы Fusion мог правильно идентифицировать нашу последовательность как последовательность Fusion, нам необходимо включитьдля нашего типа последовательности. Как обычно, мы просто создаемimplтипа, специализированного для нашего тега последовательности:

template<>
struct is_sequence_impl<example::example_sequence_tag>
{
    template<typename T>
    struct apply : mpl::true_ {};
};

У нас есть некоторые похожие формальности для завершения, обеспечиваякатегорию_of_impl, чтобы Fusion мог правильно идентифицировать наш тип последовательности, иявляется_view_impl, поэтому Fusion может правильно идентифицировать нашу последовательность как не являющуюся. Видтип. Реализации приведены в примере кода.

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

template<>
struct begin_impl<example::example_sequence_tag>
{
    template<typename Sequence>
    struct apply
    {
        typedef example::example_struct_iterator<Sequence, 0> type;
        static type
        call(Sequence& seq)
        {
            return type(seq);
        }
    };
};

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

Для нашейпоследовательности случайного доступанам также потребуется реализоватьsize_impl,value_at_implиat_impl.

Enabling our type as an associative sequence

Для того, чтобыпример_структураслужила ассоциативной передней последовательностью, нам необходимо соответствующим образом адаптировать категорию пересечения нашей последовательности и нашего итератора и включить 3 функции поиска внутренней последовательности,at_key,результат_of::значение_at_keyиимеет_key. Мы также должны включить 3 функции поиска итератора,результат_::ключ_,результат_::значение_of_dataиderef_data.

Для реализацииat_key_implнам необходимо связатьполя::названиеиполя::возрасттипы, описанные в руководствеQuick Startс соответствующими членамипример_структура. Наша реализация заключается в следующем:

template<>
struct at_key_impl<example::example_sequence_tag>
{
    template<typename Sequence, typename Key>
    struct apply;
    template<typename Sequence>
    struct apply<Sequence, fields::name>
    {
        typedef typename mpl::if_<
            is_const<Sequence>,
            std::string const&,
            std::string&>::type type;
        static type
        call(Sequence& seq)
        {
            return seq.name;
        };
    };
    template<typename Sequence>
    struct apply<Sequence, fields::age>
    {
        typedef typename mpl::if_<
            is_const<Sequence>,
            int const&,
            int&>::type type;
        static type
        call(Sequence& seq)
        {
            return seq.age;
        };
    };
};

Все это очень похоже на реализации, которые мы видели ранее, такие какderef_implиvalue_of_impl. Вместо того, чтобы идентифицировать членов по индексу или позиции, мы теперь выбираем их, используя типыполей::имяиполя::возраст. Реализации других функций одинаково просты и представлены в примере кода.

Summary

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

Поддержкаstd::пара,MPLпоследовательности иусиление::массиввсе используют один и тот же подход и предоставляют дополнительные примеры подхода для различных типов.


PrevUpHomeNext

Статья The Full Extension Mechanism раздела Chapter 1. Fusion 2.2 Extension может быть полезна для разработчиков на c++ и boost.




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



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


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-20 04:14:56/0.026937961578369/1