Библиотека 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последовательности иусиление::массиввсе используют один и тот же подход и предоставляют дополнительные примеры подхода для различных типов.