Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

5.2. Evaluating the parsed expression

[Note] Note

Note that you can find everything that has been included and defined so far here.

The final result here is a pair of the first number and the vector of the rest of the values. To calculate the result we need to process that data structure. Let's give the example output we have just parsed a name. This will make it easier to test the code calculating the final result from this structure:

> using temp_result = exp_parser7::apply<BOOST_METAPARSE_STRING("1 + 2 + 3 + 4")>::type;

Now we can write a template metafunction turning this structure into the result of the calculation this structure represents.

[Note] Note

Note that you can find everything that has been included and defined so far here.

We have a vector containing another vector. Therefore, we will need to be able to summarise the elements of different vectors. We can use the boost::mpl::fold metafunction to do this:

> #include <boost/mpl/fold.hpp>

With this metafunction, we can iterate over a vector of parsed numbers and summarise them. We can provide it a metafunction taking two arguments: the sum we have so far and the next element of the vector. This metafunction will be called for every element of the vector.

[Note] Note

Note that this is very similar to the std::accumulate algorithm. Boost.MPL provides boost::mpl::accumulate as well, which is a synonym for boost::mpl::fold. This tutorial (and Metaparse) uses the name fold.

Let's start with a simple case: a vector of numbers. For example let's summarise the elements of the following vector:

> using vector_of_numbers = \
...> boost::mpl::vector< \
...>   boost::mpl::int_<2>, \
...>   boost::mpl::int_<5>, \
...>   boost::mpl::int_<6> \
...> >;

copy-paste friendly version

We will write a template metafunction, sum_vector for summarising the elements of a vector of numbers:

> template <class Vector> \
...> struct sum_vector : \
...>    boost::mpl::fold< \
...>      Vector, \
...>      boost::mpl::int_<0>, \
...>      boost::mpl::lambda< \
...>        boost::mpl::plus<boost::mpl::_1, boost::mpl::_2> \
...>      >::type \
...>    > \
...>  {};

copy-paste friendly version

This metafunction takes the vector to summarise the elements of as its argument and uses boost::mpl::fold to calculate the sum. boost::mpl::fold takes three arguments:

  • The container to summarise. This is Vector.
  • The starting value for the sum we have so far. Using 0 means that we want to start the sum from 0.
  • The function to call in every iteration while looping over the container. We are using a lambda expression in our example, which is the expression wrapped by boost::mpl::lambda. This expression adds its two arguments together using boost::mpl::plus. The lambda expression refers to its arguments by boost::mpl::_1 and boost::mpl::_2.

Let's try this metafunction out:

> sum_vector<vector_of_numbers>::type
mpl_::integral_c<int, 13>

It works as expected. Here is a diagram showing how it works:

As the diagram shows, boost::mpl::fold evaluates the lambda expression for each element of the vector and passes the result of the previous evaluation to the next lambda expression invocation.

We have a metafunction that can summarise a vector of numbers. The result of parsing the + <number> elements is a vector of vectors. As a recap, here is temp_result:

boost_::mpl::vector<
  // The result of int_token
  mpl_::integral_c<int, 1>,

  // The result of repeated< sequence<plus_token, int_token> >
  boost_::mpl::vector<
    boost_::mpl::vector<mpl_::char_<'+'>, mpl_::integral_c<int, 2> >,
    boost_::mpl::vector<mpl_::char_<'+'>, mpl_::integral_c<int, 3> >,
    boost_::mpl::vector<mpl_::char_<'+'>, mpl_::integral_c<int, 4> >
  >
>

First let's summarise the result of repeated<...> using boost::mpl::fold. This is a vector of vectors, but that's fine. boost::mpl::fold doesn't care about what the elements of the vector are. They can be numbers, vectors or something else as well. The function we use to add two numbers together (which was a lambda expression in our previous example) gets these elements as its argument and has to deal with them. So to summarise the elements of the vectors we get as the result of parsing with repeated<...>, we need to write a metafunction that can deal with these elements. One such element is boost_::mpl::vector<mpl_::char<'+'>, mpl_::integral_c<int, 2>>. Here is a metafunction that can be used in a boost::mpl::fold:

> template <class Sum, class Item> \
...>   struct sum_items : \
...>     boost::mpl::plus< \
...>       Sum, \
...>       typename boost::mpl::at_c<Item, 1>::type \
...>     > \
...> {};

copy-paste friendly version

This function takes two arguments:

  • Sum, which is a number. This is the summary of the already processed elements.
  • Item, the next item of the vector. These items are vectors of size two: the result of parsing the + symbol and the number.

The metafunction adds the sum we have so far and the next number together using the boost::mpl::plus metafunction. To get the next number out of Item, it uses boost::mpl::at_c. Let's try sum_items out:

> sum_items< \
...>   mpl_::integral_c<int, 1>, \
...>   boost::mpl::vector<mpl_::char_<'+'>, mpl_::integral_c<int, 2>> \
...> >::type
mpl_::integral_c<int, 3>

copy-paste friendly version

We have called sum_items with values from temp_result and saw that it works as expected: it added the partial sum (mpl_::integral_c<int, 1>) to the next number (mpl_::integral_c<int, 2>).

boost::mpl::fold can summarise the list we get as the result of parsing the + <number> elements of the input, so we need to extract this list from temp_result first:

> boost::mpl::at_c<temp_result, 1>::type

Here is the formatted version of the result:

boost_::mpl::vector<
  boost_::mpl::vector<mpl_::char_<'+'>, mpl_::integral_c<int, 2>>,
  boost_::mpl::vector<mpl_::char_<'+'>, mpl_::integral_c<int, 3>>,
  boost_::mpl::vector<mpl_::char_<'+'>, mpl_::integral_c<int, 4>>
>

This is the second element of the temp_result vector (the first one is the value of the first <number> element). Let's try fold out for this:

> \
...> boost::mpl::fold< \
...>   boost::mpl::at_c<temp_result, 1>::type, /* The vector to summarise */ \
...>   boost::mpl::int_<0>, /* The value to start the sum from */ \
...>   boost::mpl::quote2<sum_items> /* The function to call in each iteration */ \
...> >::type
mpl_::integral_c<int, 9>

copy-paste friendly version

[Note] Note

We are using sum_items as the function to call in each iteration. We are passing a metafunction (sum_items) to another metafunction (boost::mpl::fold) as an argument. To be able to do this, we need to turn it into a template metafunction class using boost::mpl::quote2 (2 means that it takes two arguments).

As we have seen, the result of this is the sum of the elements, which was 9 in our case. Here is a diagram showing how boost::mpl::fold works:

It starts with the value boost::mpl::int_<0> and adds the elements of the boost_::mpl::vector containing the parsing results one by one. The diagram shows how the subresults are calculated and then used for further calculations.


PrevUpHomeNext