...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Important | |
---|---|
Before you read on please be aware that the interfaces described in this section are not finalized and may change in the future without attempting to be backwards compatible. We document the customization point interfaces anyways as we think they are important. Understanding customization points helps understanding Spirit. Additionally they prove to be powerful tools enabling full integration of the user's data structures with Qi's parsers and Karma's generators. |
Spirit has been written with
extensibility in mind. It provides many different attribute customization
points allowing to integrate custom data types with the process of parsing
in Spirit.Qi or output generation with Spirit.Karma.
All attribute customization points are exposed using a similar technique:
full or partial template specialization. Spirit
generally implements the main template, providing a default implementation.
You as the user have to provide a partial or full specialization of this
template for the data types you want to integrate with the library. In fact,
the library uses these customization points itself for instance to handle
the magic of the unused_type
attribute type.
Here is an example showing the traits::container_value
customization point
used by different parsers (such as Kleene,
Plus, etc.) to find
the attribute type to be stored in a supplied STL container:
template <typename Container, typename Enable/* = void*/> struct container_value : detail::remove_value_const<typename Container::value_type> {};
This template is instantiated by the library at the appropriate places while
using the supplied container type as the template argument. The embedded
type
is used as the attribute
type while parsing the elements to be store in that container.
The following example shows the predefined specialization for unused_type
:
template <> struct container_value<unused_type> { typedef unused_type type; };
which defines its embedded type
to be unused_type
as well,
this way propagating the 'don't care' attribute status to the embedded parser.
All attribute customization points follow the same scheme. The last template
parameter is always typename Enable = void
allowing to apply SFINAE for fine grained
control over the template specialization process. But most of the time you
can safely forget about its existence.
The following sections will describe all customization points, together with a description which needs to be specialized for what purpose.
The different customizations points are used by different parts of the library. Part of the customizations points are used by both, Spirit.Qi and Spirit.Karma, whereas others are specialized to be applied for one of the sub-libraries only. We will explain when a specific customization point needs to be implemented and, equally important, which customization points need to be implemented at the same time. Often it is not sufficient to provide a specialization for one single customization point only, in this case you as the user have to provide all necessary customizations for your data type you want to integrate with the library.