| Home | Libraries | People | FAQ | More

##### Proto's Built-In Transforms

Primitive transforms are the building blocks for more interesting composite transforms. Proto defines a bunch of generally useful primitive transforms. They are summarized below.

`proto::_value`

  Given a terminal expression, return the value of the terminal.

`proto::_child_c<>`

  Given a non-terminal expression, `proto::_child_c<`N`>` returns the `N`-th child.

`proto::_child`

  A synonym for `proto::_child_c<0>`.

`proto::_left`

  A synonym for `proto::_child_c<0>`.

`proto::_right`

  A synonym for `proto::_child_c<1>`.

`proto::_expr`

  Returns the current expression unmodified.

`proto::_state`

  Returns the current state unmodified.

`proto::_data`

  Returns the current data unmodified.

`proto::call<>`

  For a given callable transform ``CT``, `proto::call<`CT`>` turns the callable transform into a primitive transform. This is useful for disambiguating callable transforms from object transforms, and also for working around compiler bugs with nested function types.

`proto::make<>`

  For a given object transform ``OT``, `proto::make<`OT`>` turns the object transform into a primitive transform. This is useful for disambiguating object transforms from callable transforms, and also for working around compiler bugs with nested function types.

`proto::_default<>`

  Given a grammar `G`, `proto::_default<`G`>` evaluates the current node according to the standard C++ meaning of the operation the node represents. For instance, if the current node is a binary plus node, the two children will both be evaluated according to ``G`` and the results will be added and returned. The return type is deduced with the help of the Boost.Typeof library.

`proto::fold<>`

  Given three transforms ``ET``, ``ST``, and ``FT``, `proto::fold<`ET`, `ST`, `FT`>` first evaluates ``ET`` to obtain a Fusion sequence and ``ST`` to obtain an initial state for the fold, and then evaluates ``FT`` for each element in the sequence to generate the next state from the previous.

`proto::reverse_fold<>`

  Like `proto::fold<>`, except the elements in the Fusion sequence are iterated in reverse order.

`proto::fold_tree<>`

  Like `proto::fold<`ET`, `ST`, `FT`>`, except that the result of the ``ET`` transform is treated as an expression tree that is flattened to generate the sequence to be folded. Flattening an expression tree causes child nodes with the same tag type as the parent to be put into sequence. For instance, `a >> b >> c` would be flattened to the sequence [`a`, `b`, `c`], and this is the sequence that would be folded.

`proto::reverse_fold_tree<>`

  Like `proto::fold_tree<>`, except that the flattened sequence is iterated in reverse order.

`proto::lazy<>`

  A combination of `proto::make<>` and `proto::call<>` that is useful when the nature of the transform depends on the expression, state and/or data parameters. `proto::lazy<R(A0,A1...An)>` first evaluates `proto::make<R()>` to compute a callable type `R2`. Then, it evaluates `proto::call<R2(A0,A1...An)>`.

###### All Grammars Are Primitive Transforms

In addition to the above primitive transforms, all of Proto's grammar elements are also primitive transforms. Their behaviors are described below.

`proto::_`

  Return the current expression unmodified.

`proto::or_<>`

  For the specified set of alternate sub-grammars, find the one that matches the given expression and apply its associated transform.

`proto::and_<>`

  For the given set of sub-grammars, apply all the associated transforms and return the result of the last.

`proto::not_<>`

  Return the current expression unmodified.

`proto::if_<>`

  Given three transforms, evaluate the first and treat the result as a compile-time Boolean value. If it is true, evaluate the second transform. Otherwise, evaluate the third.

`proto::switch_<>`

  As with `proto::or_<>`, find the sub-grammar that matches the given expression and apply its associated transform.

`proto::terminal<>`

  Return the current terminal expression unmodified.

`proto::plus<>`, `proto::nary_expr<>`, et. al.

  A Proto grammar that matches a non-terminal such as `proto::plus<`G0`, `G1`>`, when used as a primitive transform, creates a new plus node where the left child is transformed according to ``G0`` and the right child with ``G1``.

###### The Pass-Through Transform

Note the primitive transform associated with grammar elements such as `proto::plus<>` described above. They possess a so-called pass-through transform. The pass-through transform accepts an expression of a certain tag type (say, `proto::tag::plus`) and creates a new expression of the same tag type, where each child expression is transformed according to the corresponding child grammar of the pass-through transform. So for instance this grammar ...

    proto::function< X, proto::vararg<Y> >

... matches function expressions where the first child matches the `X` grammar and the rest match the `Y` grammar. When used as a transform, the above grammar will create a new function expression where the first child is transformed according to `X` and the rest are transformed according to `Y`.

The following class templates in Proto can be used as grammars with pass-through transforms:

Table 1.11. Class Templates With Pass-Through Transforms

Templates with Pass-Through Transforms
`proto::unary_plus<>`
`proto::negate<>`
`proto::dereference<>`
`proto::complement<>`
`proto::address_of<>`
`proto::logical_not<>`
`proto::pre_inc<>`
`proto::pre_dec<>`
`proto::post_inc<>`
`proto::post_dec<>`
`proto::shift_left<>`
`proto::shift_right<>`
`proto::multiplies<>`
`proto::divides<>`
`proto::modulus<>`
`proto::plus<>`
`proto::minus<>`
`proto::less<>`
`proto::greater<>`
`proto::less_equal<>`
`proto::greater_equal<>`
`proto::equal_to<>`
`proto::not_equal_to<>`
`proto::logical_or<>`
`proto::logical_and<>`
`proto::bitwise_and<>`
`proto::bitwise_or<>`
`proto::bitwise_xor<>`
`proto::comma<>`
`proto::mem_ptr<>`
`proto::assign<>`
`proto::shift_left_assign<>`
`proto::shift_right_assign<>`
`proto::multiplies_assign<>`
`proto::divides_assign<>`
`proto::modulus_assign<>`
`proto::plus_assign<>`
`proto::minus_assign<>`
`proto::bitwise_and_assign<>`
`proto::bitwise_or_assign<>`
`proto::bitwise_xor_assign<>`
`proto::subscript<>`
`proto::if_else_<>`
`proto::function<>`
`proto::unary_expr<>`
`proto::binary_expr<>`
`proto::nary_expr<>`

###### The Many Roles of Proto Operator Metafunctions

We've seen templates such as `proto::terminal<>`, `proto::plus<>` and `proto::nary_expr<>` fill many roles. They are metafunction that generate expression types. They are grammars that match expression types. And they are primitive transforms. The following code samples show examples of each.

As Metafunctions ...

    // proto::terminal<> and proto::plus<> are metafunctions
    // that generate expression types:
    typedef proto::terminal<int>::type int_;
    typedef proto::plus<int_, int_>::type plus_;

    int_ i = {42}, j = {24};
    plus_ p = {i, j};

As Grammars ...

    // proto::terminal<> and proto::plus<> are grammars that
    // match expression types
    struct Int : proto::terminal<int> {};
    struct Plus : proto::plus<Int, Int> {};

    BOOST_MPL_ASSERT(( proto::matches< int_, Int > ));
    BOOST_MPL_ASSERT(( proto::matches< plus_, Plus > ));

As Primitive Transforms ...

    // A transform that removes all unary_plus nodes in an expression
    struct RemoveUnaryPlus
      : proto::or_<
            proto::when<
                proto::unary_plus<RemoveUnaryPlus>
              , RemoveUnaryPlus(proto::_child)
            >
            // Use proto::terminal<> and proto::nary_expr<>
            // both as grammars and as primitive transforms.
          , proto::terminal<_>
          , proto::nary_expr<_, proto::vararg<RemoveUnaryPlus> >
        >
    {};

    int main()
    {
        proto::literal<int> i(0);

        proto::display_expr(
            +i - +(i - +i)
        );

        proto::display_expr(
            RemoveUnaryPlus()( +i - +(i - +i) )
        );
    }

The above code displays the following, which shows that unary plus nodes have been stripped from the expression:

    minus(
        unary_plus(
            terminal(0)
        )
      , unary_plus(
            minus(
                terminal(0)
              , unary_plus(
                    terminal(0)
                )
            )
        )
    )
    minus(
        terminal(0)
      , minus(
            terminal(0)
          , terminal(0)
        )
    )