Библиотека Fusion предназначена для расширения, можно легко добавлять новые типы последовательностей. На самом деле, библиотека поддерживаетstd:Пара
,повышение:См.
иMPLпоследовательности полностью обеспечиваются с использованием механизма расширения.
Процесс добавления нового типа последовательности в Fusion:
- Включите механизм отправки меток, используемый Fusion для вашего типа последовательности
- Проектирование типа итератора для последовательности
- Обеспечить специализированное поведение для внутренних операций новой последовательности Fusion
Чтобы проиллюстрировать возможность нового типа последовательности для использования с 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.
Механизм расширяемости Fusion используетотправку метокдля вызова правильного кода для данного типа последовательности. Чтобы использовать механизм отправки тегов, мы должны сначала объявить новый тип тегов для использования механизма. Например:
namespace example {
struct example_sequence_tag;
}
Далее нам нужно включитьпризнаки::тег_
метафункции, чтобы вернуть наш вновь выбранный тип тега для операций, включающих нашу последовательность. Это делается путем специализациичерт::тег_
для нашего типа последовательности.
#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>
Нам нужен итератор для описания позиций и обеспечения доступа к данным в нашей последовательности. Как это легко сделать, мы собираемся предоставить итератор случайного доступа в нашем примере.
Мы будем использовать простую конструкцию, в которой 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_;
};
Краткое изложение деталей нашего итератора:
- Итератор параметризируется по типу, который он повторяет, и индексу текущего элемента.
- Typedefs
struct_type
иindex
обеспечивают удобный доступ к информации, которая нам понадобится позже в реализации.
- Категория typedef
позволяетчертам::категории_
метафункции для установления категории прохождения итератора.
- Конструктор сохраняет ссылку на
пример_структура
, который повторяется.
Нам также необходимо включитьотправку теговдля нашего типа итератора с другой специализациейпризнаков::тег_of
.
В изоляции реализация итератора довольно сухая. Все должно стать яснее, поскольку мы добавляем функции в нашу реализацию.
Для начала мы получимрезультат_ из::значение_ из
Работающая метафункция. Для этого мы предоставляем специализацию ускорения::слияния::расширения::значение_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]](/img/note.png) |
Note |
Несмотря на то, что осталось сделать достаточное количество для получения полноценной последовательности Fusion,результат_::значение_ иотмена иллюстрируют все существенные требуемые понятия. Остальная часть процесса очень повторяется, просто требуя реализации подходящегоxxxx_impl для каждого признакаxxxx . |
Хорошо, теперь мы видели, какрезультат_::значение_
иотмена
работают, все остальное будет работать почти таким же образом. Начнем с прямой итерации, предоставив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
приведены в примере кода.
Чтобы 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
.
Для того, чтобыпример_структура
служила ассоциативной передней последовательностью, нам необходимо соответствующим образом адаптировать категорию пересечения нашей последовательности и нашего итератора и включить 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
. Вместо того, чтобы идентифицировать членов по индексу или позиции, мы теперь выбираем их, используя типыполей::имя
иполя::возраст
. Реализации других функций одинаково просты и представлены в примере кода.
Теперь мы проработали весь процесс добавления новой последовательности случайного доступа, и мы также позволили нашему типу служить ассоциативной последовательностью. Реализация была немного длинной, но следовала простой повторяющейся схеме.
Поддержкаstd::пара
,MPLпоследовательности иусиление::массив
все используют один и тот же подход и предоставляют дополнительные примеры подхода для различных типов.