forked from boostorg/fusion
adding fusion docs and tests
[SVN r34920]
This commit is contained in:
398
doc/extension.qbk
Normal file
398
doc/extension.qbk
Normal file
@ -0,0 +1,398 @@
|
||||
[section Extension]
|
||||
|
||||
The Fusion library is designed to be extensible, new sequences types can easily
|
||||
be added. In fact, the library support for `std::pair`, `boost::array` and __mpl__
|
||||
sequences is entirely provided using the extension mechanism.
|
||||
|
||||
The process for adding a new sequence type to Fusion is:
|
||||
|
||||
# Enable the __tag_dispatching__ mechanism used by Fusion for your sequence type
|
||||
# Design an iterator type for the sequence
|
||||
# Provide specialized behaviour for the intrinsic operations of the new Fusion sequence
|
||||
|
||||
[heading Our example]
|
||||
|
||||
In order to illustrate enabling a new sequence type for use with Fusion, we
|
||||
are going to use the type:
|
||||
|
||||
namespace example
|
||||
{
|
||||
struct example_struct
|
||||
{
|
||||
std::string name;
|
||||
int age;
|
||||
example_struct(
|
||||
const std::string& n,
|
||||
int a)
|
||||
: name(n), age(a)
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
We are going to pretend that this type has been provided by a 3rd party
|
||||
library, and therefore cannot be modified. We shall work through all the
|
||||
necessary steps to enable `example_struct` to serve as an __associative_sequence__
|
||||
as described in the __quick_start__ guide.
|
||||
|
||||
[heading Enabling Tag Dispatching]
|
||||
|
||||
The Fusion extensibility mechanism uses __tag_dispatching__ to call the
|
||||
correct code for a given sequence type. In order to exploit the tag
|
||||
dispatching mechanism we must first declare a new tag type for the
|
||||
mechanism to use. For example:
|
||||
|
||||
namespace boost { namespace fusion {
|
||||
struct example_sequence_tag; // Only definition needed
|
||||
}}
|
||||
|
||||
Next we need to enable the `traits::tag_of` metafunction to return our newly chosen
|
||||
tag type for operations involving our sequence. This is done by specializing
|
||||
`traits::tag_of` for our sequence type.
|
||||
|
||||
#include <boost/fusion/support/tag_of_fwd.hpp>
|
||||
|
||||
namespace boost { namespace fusion { namespace traits {
|
||||
|
||||
template<>
|
||||
struct tag_of<example_struct>
|
||||
{
|
||||
typedef example_sequence_tag type;
|
||||
};
|
||||
}}}
|
||||
|
||||
`traits::tag_of` also has a second template argument,
|
||||
that can be used in conjuction with `boost::enable_if` to provide tag
|
||||
support for whole clases of types. This feature is not necessary
|
||||
for our sequence, but for an example see the code in:
|
||||
|
||||
#include <boost/fusion/sequence/adapted/mpl/tag_of.hpp>
|
||||
|
||||
[heading Designing a suitable iterator]
|
||||
|
||||
We need an iterator to describe positions, and provide access to
|
||||
the data within our sequence. As it is straightforward to do,
|
||||
we are going to provide a random access iterator in our example.
|
||||
|
||||
We will use a simple design, in which the 2 members of
|
||||
`example_struct` are given numbered indices, 0 for `name` and
|
||||
1 for `age` respectively.
|
||||
|
||||
template<typename Struct, int Pos>
|
||||
struct example_struct_iterator
|
||||
: iterator_base<example_struct_iterator<Struct, Pos> >
|
||||
{
|
||||
BOOST_STATIC_ASSERT(Pos >=0 && Pos < 3);
|
||||
typedef Struct struct_type;
|
||||
typedef mpl::int_<Pos> index;
|
||||
typedef random_access_traversal_tag category;
|
||||
|
||||
example_struct_iterator(Struct& str)
|
||||
: struct_(str) {}
|
||||
|
||||
Struct& struct_;
|
||||
};
|
||||
|
||||
A quick summary of the details of our iterator:
|
||||
|
||||
# The iterator is parameterized by the type it is iterating over, and the index of the current element.
|
||||
# The typedefs `struct_type` and `index` provide convenient access to information we will need later in
|
||||
the implementation.
|
||||
# The typedef `category` allows the `traits::__category_of__` metafunction to establish
|
||||
the traversal category of the iterator.
|
||||
# The constructor stores a reference to the `example_struct` being iterated over.
|
||||
|
||||
We also need to enable __tag_dispatching__ for our iterator type, with another specialization of
|
||||
`traits::tag_of`:
|
||||
|
||||
namespace boost { namespace fusion {
|
||||
struct example_struct_iterator_tag;
|
||||
|
||||
namespace traits
|
||||
{
|
||||
template<typename Struct, int Pos>
|
||||
struct tag_of<boost::fusion::example_struct_iterator<Struct, Pos> >
|
||||
{
|
||||
typedef example_struct_iterator_tag type;
|
||||
};
|
||||
}
|
||||
}}
|
||||
|
||||
In isolation, the iterator implementation is pretty dry. Things should become clearer as we
|
||||
add features to our implementation.
|
||||
|
||||
[heading A first couple of instructive features]
|
||||
|
||||
To start with, we will get the __result_of_value_of__ metafunction working. To
|
||||
do this, we provide a specialization of the `boost::fusion::extension::value_of_impl` template for
|
||||
our iterator's tag type.
|
||||
|
||||
template<>
|
||||
struct value_of_impl<example_struct_iterator_tag>
|
||||
{
|
||||
template<typename Iterator>
|
||||
struct apply;
|
||||
|
||||
template<typename Struct>
|
||||
struct apply<example_struct_iterator<Struct, 0> >
|
||||
{
|
||||
typedef std::string type;
|
||||
};
|
||||
|
||||
template<typename Struct>
|
||||
struct apply<example_struct_iterator<Struct, 1> >
|
||||
{
|
||||
typedef int type;
|
||||
};
|
||||
};
|
||||
|
||||
The implementation itself is pretty simple, it just uses 2 partial specializations to
|
||||
provide the type of the 2 different members of `example_struct`, based on the index of the iterator.
|
||||
|
||||
To understand how `value_of_impl` is used by the library we will look at the implementation of __value_of__:
|
||||
|
||||
template <typename Iterator>
|
||||
struct __value_of__
|
||||
{
|
||||
typedef typename
|
||||
extension::value_of_impl<typename Iterator::ftag>::
|
||||
template apply<Iterator>::type
|
||||
type;
|
||||
};
|
||||
|
||||
So __value_of__ uses __tag_dispatching__ to select an __mpl_metafunction_class__
|
||||
to provide its functionality. You will notice this pattern throughout the
|
||||
implementation of Fusion.
|
||||
|
||||
Ok, lets enable dereferencing of our iterator. In this case we must provide a suitable
|
||||
specialization of `deref_impl`.
|
||||
|
||||
template<>
|
||||
struct deref_impl<example_struct_iterator_tag>
|
||||
{
|
||||
template<typename Iterator>
|
||||
struct apply;
|
||||
|
||||
template<typename Struct>
|
||||
struct apply<example_struct_iterator<Struct, 0> >
|
||||
{
|
||||
typedef typename mpl::if_<
|
||||
is_const<Struct>, std::string const&, std::string&>::type type;
|
||||
|
||||
static type
|
||||
call(example_struct_iterator<Struct, 0> const& it)
|
||||
{
|
||||
return it.struct_.name;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Struct>
|
||||
struct apply<example_struct_iterator<Struct, 1> >
|
||||
{
|
||||
typedef typename mpl::if_<
|
||||
is_const<Struct>, int const&, int&>::type type;
|
||||
|
||||
static type
|
||||
call(example_struct_iterator<Struct, 1> const& it)
|
||||
{
|
||||
return it.struct_.age;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
The use of `deref_impl` is very similar to that of `value_of_impl`, but it also
|
||||
provides some runtime functionality this time via the `call` static member function.
|
||||
To see how `deref_impl` is used, lets have a look at the implementation of __deref__:
|
||||
|
||||
namespace result_of
|
||||
{
|
||||
template <typename Iterator>
|
||||
struct __deref__
|
||||
{
|
||||
typedef typename
|
||||
deref_impl<typename Iterator::ftag>::
|
||||
template apply<Iterator>::type
|
||||
type;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
typename __result_of_deref__<Iterator>::type
|
||||
__deref__(Iterator const& i)
|
||||
{
|
||||
typename __result_of_deref__<Iterator>::type result =
|
||||
extension::deref_impl<typename Iterator::ftag>::
|
||||
template apply<Iterator>::call(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
So again __result_of_deref__ uses __tag_dispatching__ in exactly the
|
||||
same way as the __value_of__ implementation. The runtime functionality used
|
||||
by __deref__ is provided by the `call` static function of the selected
|
||||
__mpl_metafunction_class__.
|
||||
|
||||
The actual implementation of `deref_impl` is slightly more complex than that of `value_of_impl`.
|
||||
We also need to implement the `call` function, which returns a reference
|
||||
to the appropriate member of the underlying sequence. We also require a little
|
||||
bit of metaprogramming to return `const` references if the underlying sequence
|
||||
is const.
|
||||
|
||||
[note Although there is a fair amount of left to do to produce a fully fledged
|
||||
Fusion sequence, __value_of__ and __deref__ illustrate all the signficant concepts
|
||||
required. The remainder of the process is very repetitive, simply requiring
|
||||
implementation of a suitable `xxxx_impl` for each feature `xxxx`.
|
||||
]
|
||||
|
||||
[heading Implementing the remaining iterator functionality]
|
||||
|
||||
Ok, now we have seen the way __value_of__ and __deref__ work, everything else will work
|
||||
in pretty much the same way. Lets start with forward iteration,
|
||||
by providing a `next_impl`:
|
||||
|
||||
template<>
|
||||
struct next_impl<example_struct_iterator_tag>
|
||||
{
|
||||
template<typename Iterator>
|
||||
struct apply
|
||||
{
|
||||
typedef typename Iterator::struct_type struct_type;
|
||||
typedef typename Iterator::index index;
|
||||
typedef example_struct_iterator<struct_type, index::value + 1> type;
|
||||
|
||||
static type
|
||||
call(Iterator const& i)
|
||||
{
|
||||
return type(i.struct_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
This should be very familiar from our `deref_impl` implementation, we will be
|
||||
using this approach again and again now. Our design is simply to increment
|
||||
the `index` counter to move on to the next element. The various other iterator
|
||||
manipulations we need to perform will all just involve simple calculations
|
||||
with the `index` variables.
|
||||
|
||||
We also need to provide a suitable `equal_to_impl` so that iterators can be
|
||||
correctly compared. A __bidirectional_iterator__ will also need an implementation of `prior_impl`. For a
|
||||
__random_access_iterator__ `distance_impl` and `advance_impl` also need to be provided
|
||||
in order to satisfy the necessary complexity guarantees. As our iterator is
|
||||
a __random_access_iterator__ we will have to implement all of these functions.
|
||||
|
||||
Full implementations of `prior_impl`, `advance_impl`, `distance_impl` and `equal_to_impl` are
|
||||
provided in the example code.
|
||||
|
||||
[heading Implementing the intrinsic functions of the sequence]
|
||||
|
||||
In order that Fusion can correctly identify our sequence as a Fusion sequence, we
|
||||
need to enable `is_sequence` for our sequence type. As usual we just create
|
||||
an `impl` type specialized for our sequence tag:
|
||||
|
||||
template<>
|
||||
struct is_sequence_impl<example_sequence_tag>
|
||||
{
|
||||
template<typename T>
|
||||
struct apply : mpl::true_ {};
|
||||
};
|
||||
|
||||
We've some similar formalities to complete, providing `category_of_impl` so Fusion
|
||||
can correctly identify our sequence type, and `is_view_impl` so Fusion can correctly
|
||||
identify our sequence as not being a __view__ type. Implementations are
|
||||
provide in the example code.
|
||||
|
||||
Now we've completed some formalities, on to more interesting features. Lets get
|
||||
__begin__ working so that we can get an iterator to start accessing the data in
|
||||
our sequence.
|
||||
|
||||
template<>
|
||||
struct begin_impl<example_sequence_tag>
|
||||
{
|
||||
template<typename Sequence>
|
||||
struct apply
|
||||
{
|
||||
typedef example_struct_iterator<Sequence, 0> type;
|
||||
|
||||
static type
|
||||
call(Sequence& seq)
|
||||
{
|
||||
return type(seq);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
The implementation uses the same ideas we have applied throughout, in this case
|
||||
we are just creating one of the iterators we developed earlier, pointing to the
|
||||
first element in the sequence. The implementation of __end__ is very similar, and
|
||||
is provided in the example code.
|
||||
|
||||
For our __random_access_sequence__ we will also need to implement `size_impl`,
|
||||
`value_at_impl` and `at_impl`.
|
||||
|
||||
[heading Enabling our type as an associative container]
|
||||
|
||||
In order for `example_struct` to serve as an associative container,
|
||||
we need to enable 3 lookup features, __at_key__, __value_at_key__ and __has_key__.
|
||||
We also need to provide an implementation of the `is_associative` trait
|
||||
so that our sequence can be correctly identified as an associative container.
|
||||
|
||||
To implement `at_key_impl` we need to associate the `fields::age` and `fields::age`
|
||||
types described in the __quick_start__ guide with the appropriate members of `example_struct`.
|
||||
Our implementation is as follows:
|
||||
|
||||
template<>
|
||||
struct at_key_impl<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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Its all very similar to the implementations we've seen previously,
|
||||
such as `deref_impl` and `value_of_impl`. Instead of identifying
|
||||
the members by index or position, we are now selecting them using
|
||||
the types `fields::name` and `fields::age`. The implementations of
|
||||
`value_at_key_impl` and `has_key_impl` are equally straightforward,
|
||||
and are provided in the example code, along with an implementation
|
||||
of `is_associative_impl`.
|
||||
|
||||
[heading Summary]
|
||||
|
||||
We've now worked through the entire process for adding a new random
|
||||
access sequence and we've also enabled our type to serve as an associative
|
||||
container. The implementation was slightly longwinded, but followed
|
||||
a simple repeating pattern.
|
||||
|
||||
The support for `std::pair`, __mpl__ sequences, and `boost::array` all
|
||||
use the same approach, and provide additional examples of the approach
|
||||
for a variety of types.
|
||||
|
||||
[endsect]
|
||||
|
Reference in New Issue
Block a user