...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The template container_iterator
is a template meta-function used as an attribute customization point.
It is invoked by the Karma repetitive generators
(such as List (%
), Kleene
(unary *
), Plus (unary +
), and Repeat)
in order to determine the type of the iterator to use to iterate over
the items to be exposed as the elements of a container.
#include <boost/spirit/home/support/container.hpp>
Also, see Include Structure.
Note | |
---|---|
This header file does not need to be included directly by any user program as it is normally included by other Spirit header files relying on its content. |
Name |
---|
|
template <typename Container, typename Enable> struct container_iterator { typedef <unspecified> type; };
Parameter |
Description |
Default |
---|---|---|
|
The type, |
none |
|
Helper template parameter usable to selectively enable or disable
certain specializations of |
|
Notation
C
A container type the iterator type needs to be evaluated for.
Expression |
Semantics |
---|---|
|
Result of the metafunction that evaluates the type to be used
as the iterator for accessing all elements of a container,
|
The returned type conceptually needs to be equivalent to a standard forward iterator. But it does not have to expose the standardized interface. If this customization point is implemented for a certain container type, all related customization points need to be implemented as well (see Related Attribute Customization Points below). This encapsulates the specific iterator interface required for a given type. The minimal requirements for a type to be exposed as an iterator in this context are:
traits::compare_iterators
),
traits::next_iterator
),
traits::deref_iterator
).
Spirit predefines specializations
of this customization point for several types. The following table lists
those types together with the types returned by the embedded typedef
type
:
Template Parameters |
Semantics |
---|---|
|
Returns |
|
Returns |
|
Returns |
The customization point container_iterator
needs to be implemented for a specific type whenever this type is to
be used as an attribute in place of a STL container. It is applicable
for generators (Spirit.Karma) only. As a rule of
thumb: it has to be implemented whenever a certain type is to be passed
as an attribute to a generator normally exposing a STL container, C
and if the type does not expose the
interface of a STL container (i.e. is_container<C>::type
would normally return mpl::false_
).
If this customization point is implemented, the following other customization points might need to be implemented as well.
Name |
When to implement |
---|---|
Needs to be implemented whenever a type is to be used as a container attribute in Karma. |
|
Karma: List
( |
|
Karma: List
( |
|
Karma: List
( |
|
Karma: List
( |
|
Karma: List
( |
|
Karma: List
( |
Here are the header files needed to make the example code below compile:
#include <boost/spirit/include/karma.hpp> #include <iostream> #include <vector>
The example (for the full source code please see here: customize_embedded_container.cpp) uses the data structure
namespace client { struct embedded_container { // expose the iterator of the embedded vector as our iterator typedef std::vector<int>::const_iterator iterator; // expose the type of the held data elements as our type typedef std::vector<int>::value_type type; // this is the vector holding the actual elements we need to generate // output from std::vector<int> data; }; }
as a direct container attribute to the List
(%
) generator. In
order to make this data structure compatible we need to specialize a
couple of attribute customization points: traits::is_container
, traits::container_iterator
, traits::begin_container
, and traits::end_container
. As you can see
the specializations simply expose the embedded std::vector<int>
as the container to use. We don't
need to specialize the customization points related to iterators (traits::deref_iterator
, traits::next_iterator
, and traits::compare_iterators
) as we expose
a standard iterator and the default implementation of these customizations
handles standard iterators out of the box.
// All specializations of attribute customization points have to be placed into // the namespace boost::spirit::traits. // // Note that all templates below are specialized using the 'const' type. // This is necessary as all attributes in Karma are 'const'. namespace boost { namespace spirit { namespace traits { // The specialization of the template 'is_container<>' will tell the // library to treat the type 'client::embedded_container' as a // container holding the items to generate output from. template <> struct is_container<client::embedded_container const> : mpl::true_ {}; // The specialization of the template 'container_iterator<>' will be // invoked by the library to evaluate the iterator type to be used // for iterating the data elements in the container. We simply return // the type of the iterator exposed by the embedded 'std::vector<int>'. template <> struct container_iterator<client::embedded_container const> { typedef client::embedded_container::iterator type; }; // The specialization of the templates 'begin_container<>' and // 'end_container<>' below will be used by the library to get the iterators // pointing to the begin and the end of the data to generate output from. // These specializations simply return the 'begin' and 'end' iterators as // exposed by the embedded 'std::vector<int>'. // // The passed argument refers to the attribute instance passed to the list // generator. template <> struct begin_container<client::embedded_container const> { static client::embedded_container::iterator call(client::embedded_container const& d) { return d.data.begin(); } }; template <> struct end_container<client::embedded_container const> { static client::embedded_container::iterator call(client::embedded_container const& d) { return d.data.end(); } }; }}}
The last code snippet shows an example using an instance of the data
structure client::embedded_container
to generate output
from a List (%
) generator:
client::embedded_container d1; // create some test data d1.data.push_back(1); d1.data.push_back(2); d1.data.push_back(3); // use the instance of an 'client::embedded_container' instead of a // STL vector std::cout << karma::format(karma::int_ % ", ", d1) << std::endl; // prints: '1, 2, 3'
As you can see, the specializations for the customization points as defined above enable the seamless integration of the custom data structure without having to modify the output format or the generator itself.
For other examples of how to use the customization point container_iterator
please see here:
use_as_container
and counter_example.