All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Pages
Components

Motivation

The components class is boost.fusion.map-like class with constant set of keys (names) that supports default values for each key.

This class is used in the design of template function, that instead of taking several arguments takes one components class representing these arguments (see Motivating example for more details). One can think of the components class as a tuple containing these arguments.

This way of designing a template function provides many benefits. The arguments are binded together, this feature is valuable when the function arguments depend on each other. While the arguments are grouped together into one components class, one can easily replace one of them (see Replacing Components for more details).

There are more benefits of this design. Assume that one is implementing a template function and wants to provide several configurations of the default arguments and permits the function's user to easily create the new configuration of the defaults arguments. This can be easily done using the components class (see Motivating example for more details).

Motivating example

Suppose that we would like to write a function which takes three functors: Init, Start and Stop. We are going to construct an interface with usage of the components class.

Very basic usage:

The interface of the function has to be defined in the following way:

    template <typename DoStuffComponents>
    void do_stuff(DoStuffComponents comps);

we need to define the names of the needed components:

    struct Init;
    struct Start;
    struct Stop;

and specify the appropriate components class:

    template <typename... Args>
    using DoStuffComponents = Components<Init, Start, Stop>::type<Args...>;

In order to use the function, the user needs to implement InitImpl, StartImpl, StopImpl functors. Then the function can be called as follows:

    DoStuffComponents<InitImpl, StartImpl, StopImpl> doStuffComponents;
    do_stuff(doStuffComponents);

Here, we have assumed that all the implementations have default constructors, however, this is not mandatory. There are different ways of initializing components (see section: Constructing Components for more details).

Let us now discuss how to access the parameters in DoStuffComponets:

    doStuffComponents.get<Init>(); //getting Init component
    InitImpl anotherImplementation(42);
    doStuffComponents.set<Init>(anotherImplementation); //setting Init component
    doStuffComponents.call<Start>("hello world"); // you can directly call a component if it is a functor

How to provide default parameters:

You can define DoStuffComponents as follows:

    template <typename... Args>
    using DoStuffComponents = Components<Init, NameWithDefault<Start, DefaultStart>, NameWithDefault<Stop, DefaultStop>>::type<Args...>;

A user can construct the DoStuffComponents as follows:

    DoStuffComponents<InitImpl> doStuffComponents1;
    DoStuffComponents<InitImpl, StartImpl> doStuffComponents2;
    DoStuffComponents<InitImpl, StartImpl, StopImpl> doStuffComponents3;

The above example illustrates the main motivation for the components class. The library also provides much more handy ways of manipulating the components, which will be described in the following sections.

Defining Components

A components can have any type including reference types. Default parameters can be specified for any number of components (when a component has a default value then also all following components must have a default value). The components class can be defined using template aliasing (this is the preferred way):

    //inside library:
    template <typename... Args> using DoStuffComponents = Components<Init, Start, Stop>::type<Args...>;
    //user:
    DoStuffComponents<InitImpl, BeginImpl, StopImpl> doStuffComponents;

This can also be done without template aliasing:

    //inside library:
    typedef Components<Init, Start, Stop> DoStuffComponents;
    //user:
    DoStuffComponents::type<InitImpl, BeginImpl, StopImpl> doStuffComponents;

Constructing Components

There are several ways of constructing components:

The important thing is that a component does not need to have a default constructor unless the default constructor is actually used.

Replacing Components

The components can be replaced in a given components instance. A new instance is created this way:

    template <typename... Args>
    using DoStuffComponents = Components<Init, Start, Stop>::type<Args...>;
    typedef DoStuffComponents<int, int, int> SomeDoStuffComps;
    typedef ReplacedType<Start, double, SomeDoStuffComps>::type Replaced; // Start type is changed from int to double
    SomeDoStuffComps comps;
    double d(5);
    Replaced replaced = replace<Start>(d, comps); //replacing component

Comparison to other libraries

Supported Compilers

MSVC is not supported due to lack of template aliasing.