Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

Complex - Made easier

In the previous section we showed how to format a complex number (i.e. a pair of doubles). In this section we will build on this example with the goal to avoid using semantic actions in the format specification. Let's have a look at the resulting code first, trying to understand it afterwards (the full source file for this example can be found here: complex_number_easier.cpp):

template <typename OutputIterator>
bool generate_complex(OutputIterator sink, std::complex<double> const& c)
{
    using boost::spirit::karma::double_;
    using boost::spirit::karma::omit;
    using boost::spirit::karma::generate;

    return generate(sink,

        //  Begin grammar
        (
           !double_(0.0) << '(' << double_ << ", " << double_ << ')'
        |   omit[double_] << double_
        ),
        //  End grammar

        c.imag(), c.real(), c.imag()     //  Data to output
    );
}

Let's cover some basic library features first.

Making Numeric Generators Fail

All Numeric Generators (such as double_, et.al.) take the value to emit from an attached attribute.

double d = 1.5;
generate(out, double_, d);       // will emit '1.5' (without the quotes)

Alternatively, they may be initialized from a literal value. For instance, to emit a constant 1.5 you may write:

generate(out, double_(1.5));     // will emit '1.5' as well (without the quotes)

The difference to a simple 1.5 or lit(1.5) is that the double_(1.5) consumes an attribute if one is available. Additionally, it compares its immediate value to the value of the supplied attribute, and fails if those are not equal.

double d = 1.5;
generate(out, double_(1.5), d);  // will emit '1.5' as long as d == 1.5

This feature, namely to succeed generating only if the attribute matches the immediate value, enables numeric generators to be used to dynamically control the way output is generated.

[Note] Note

Quite a few generators will fail if their immediate value is not equal to the supplied attribute. Among those are all Character Generators and all String Generators. Generally, all generators having a sibling created by a variant of lit() belong into this category.

Predicates - The Conditionals for Output Generators

In addition to the eps generator mentioned earlier Spirit.Karma provides two special operators enabling dynamic flow control: the And predicate (unary &) and the Not predicate (unary !). The main property of both predicates is to discard all output emitted by the generator they are attached to. This is equivalent to the behaviour of predicates used for parsing. There the predicates do not consume any input allowing to look ahead in the input stream. In Karma, the and predicate succeeds as long as its associated generator suceeds, while the not predicate succeeds only if its associated generator fails.

[Note] Note

The generator predicates in Spirit.Karma consume an attribute, if available. This makes them behave differently from predicates in Spirit.Qi, where they do not expose any attribute. This is because predicates allow to make decisions based on data available only at runtime. While in Spirit.Qi during parsing the decision is made based on looking ahead a few more input tokens, in Spirit.Karma the criteria has to be supplied by the user. The simplest way to do this is by providing an attribute.

As an example, the following generator succeeds generating

double d = 1.0;
BOOST_ASSERT(generate(out, &double_(1.0), d));    // succeeds as d == 1.0

while this one will fail:

double d = 1.0;
BOOST_ASSERT(!generate(out, !double_(1.0), d));   // fails as d == 1.0

Neither of these will emit any output. The predicates discard everything emitted by the generators they are applied to.

Ignoring Supplied Attributes

Sometimes it is desirable to 'skip' (i.e. ignore) a provided attribute. This happens for instance in alternative generators, where some of the alternatives need to extract only part of the overall attribute passed to the alternative generator. Spirit.Karma has a special pseudo generator for that: the directive omit[]. This directive consumes an attribute of the type defined by its embedded generator but it does not emit any output.

[Note] Note

The Spirit.Karma omit directive does the 'opposite' of the directive of the same name in Spirit.Qi. While the omit in Spirit.Qi consumes input without exposing an attribute, its Spirit.Karma counterpart consumes an attribute without emitting any output.

Putting everything together

Very similar to our first example ealier we use two alternatives to allow for the two different output formats depending on whether the imaginery part of the complex number is equal to zero or not. The first alternative is executed if the imaginary part is not zero, the second alternative otherwise. This time we make the decision during runtime using the Not predicate (unary !) combined with the feature of many Karma primitive generators to fail under certain conditions. Here is the first alternative again for your reference:

!double_(0.0) << '(' << double_ << ", " << double_ << ')'

The generator !double_(0.0) does several things. First, because of the Not predicate (unary !), it succeeds only if the double_(0.0) generator fails, making the whole first alternative fail otherwise. Second, the double_(0.0) generator succeeds only if the value of its attribute is equal to its immediate parameter (i.e. in this case 0.0). And third, the not predicate does not emit any output (regardless whether it succeeds or fails), discarding any possibly emitted output from the double_(0.0).

As we pass the imaginery part of the complex number as the attribute value for the !double_(0.0), the overall first alternative will be chosen only if it is not equal to zero (the !double_(0.0) does not fail). That is exactly what we need!

Now, the second alternative has to emit the real part of the complex number only. In order to simplify the overall grammar we strive to unify the attribute types of all alternatives. As the attribute type exposed by the first alternative is tuple<double, double, double>, we need to skip the first and last element of the attribute (remember, we pass the real part as the second attribute element). We achieve this by using the omit[] directive:

omit[double_] << double_ << omit[double_]

The overall attribute of this expression is tuple<double, double, double>, but the omit[] 'eats up' the first and the last element. The output emitted by this expression consist of a single generated double representing the second element of the tuple, i.e. the real part of our complex number.

[Important] Important

Generally, it is preferrable to use generator constructs not requiring semantic actions. The reason is that semantic actions often use constructs like: double_[_1 = c.real()]. But this assignment is a real one! The data is in fact copied to the attribute value of the generator the action is attached to. On the other hand, grammars without any semantic actions usually don't have to copy the attributes, making them more efficient.


PrevUpHomeNext