| Home | Libraries | People | FAQ | More

##### Controlling How Child Expressions Are Captured

| Note
This is an advanced topic. Feel free to skip this if you're just getting started with Proto.

Proto's operator overloads build expressions from sub-expressions. The sub-expressions become children of the new expression. By default, the children are stored in the parent by reference. This section describes how to change that default.

###### Primer: `as_child` vs. `as_expr`

Proto lets you independently customize the behavior of `proto::as_child()` and `proto::as_expr()`. Both accept an object `x` and return a Proto expression by turning `x` it into a Proto terminal if necessary. Although similar, the two functions are used in different situations and have subtly different behavior by default. It's important to understand the difference so that you know which to customize to achieve the behavior you want.

To wit: `proto::as_expr()` is typically used by you to turn an object into a Proto expression that is to be held in a local variable, as so:

    auto l = proto::as_expr(x); // Turn x into a Proto expression, hold the result in a local

The above works regardless of whether `x` is already a Proto expression or not. The object `l` is guaranteed to be a valid Proto expression. If `x` is a non-Proto object, it is turned into a terminal expression that holds `x` by value.[2] If `x` is a Proto object already, `proto::as_expr()` returns it by value unmodified.

In contrast, `proto::as_child()` is used internally by Proto to pre-process objects before making them children of another expression. Since it's internal to Proto, you don't see it explicitly, but it's there behind the scenes in expressions like this:

    x + y; // Consider that y is a Proto expression, but x may or may not be.

In this case, Proto builds a plus node from the two children. Both are pre-processed by passing them to `proto::as_child()` before making them children of the new node. If `x` is not a Proto expression, it becomes one by being wrapped in a Proto terminal that holds it by reference. If `x` is already a Proto expression, `proto::as_child()` returns it by reference unmodified. Contrast this with the above description for `proto::as_expr()`.

The table below summarizes the above description.

Table 1.3. proto::as_expr() vs. proto::as_child()

Function | When `t` is not a Proto expr... | When `t` is a Proto expr...
--- | --- | ---
`proto::as_expr(t)` | Return (by value) a new Proto terminal holding `t` by value. | Return `t` by value unmodified.
`proto::as_child(t)` | Return (by value) a new Proto terminal holding `t` by reference. | Return `t` by reference unmodified.

| Note
There is one important place where Proto uses both `as_expr` and `as_child`: `proto::make_expr()`. The `proto::make_expr()` function requires you to specify for each child whether it should be held by value or by reference. Proto uses `proto::as_expr()` to pre-process the children to be held by value, and `proto::as_child()` for the ones to be held by reference.

Now that you know what `proto::as_child()` and `proto::as_expr()` are, where they are used, and what they do by default, you may decide that one or both of these functions should have different behavior for your domain. For instance, given the above description of `proto::as_child()`, the following code is always wrong:

    proto::literal<int> i(0);
    auto l = i + 42; // This is WRONG! Don't do this.

Why is this wrong? Because `proto::as_child()` will turn the integer literal 42 into a Proto terminal that holds a reference to a temporary integer initialized with 42. The lifetime of that temporary ends at the semicolon, guaranteeing that the local `l` is left holding a dangling reference to a deceased integer. What to do? One answer is to use `proto::deep_copy()`. Another is to customize the behavior of `proto::as_child()` for your domain. Read on for the details.

###### Per-Domain `as_child`

To control how Proto builds expressions out of sub-expressions in your domain, define your domain as usual, and then define a nested `as_child<>` class template within it, as follows:

    class my_domain
      : proto::domain< my_generator, my_grammar >
    {
        // Here is where you define how Proto should handle
        // sub-expressions that are about to be glommed into
        // a larger expression.
        template< typename T >
        struct as_child
        {
            typedef unspecified-Proto-expr-type result_type;

            result_type operator()( T & t ) const
            {
                return unspecified-Proto-expr-object;
            }
        };
    };

There's one important thing to note: in the above code, the template parameter `T` may or may not be a Proto expression type, but the result must be a Proto expression type, or a reference to one. That means that most user-defined `as_child<>` templates will need to check whether `T` is an expression or not (using `proto::is_expr<>`), and then turn non-expressions into Proto terminals by wrapping them as `proto::terminal< /* ... */ >::type` or equivalent.

###### Per-Domain `as_expr`

Although less common, Proto also lets you customize the behavior of `proto::as_expr()` on a per-domain basis. The technique is identical to that for `as_child`. See below:

    class my_domain
      : proto::domain< my_generator, my_grammar >
    {
        // Here is where you define how Proto should handle
        // objects that are to be turned into expressions
        // fit for storage in local variables.
        template< typename T >
        struct as_expr
        {
            typedef unspecified-Proto-expr-type result_type;

            result_type operator()( T & t ) const
            {
                return unspecified-Proto-expr-object;
            }
        };
    };

###### Making Proto Expressions `auto`-safe

Let's look again at the problem described above involving the C++11 `auto` keyword and the default behavior of `proto::as_child()`.

    proto::literal<int> i(0);
    auto l = i + 42; // This is WRONG! Don't do this.

Recall that the problem is the lifetime of the temporary integer created to hold the value 42. The local `l` will be left holding a dangling reference to it after its lifetime is over. What if we want Proto to make expressions safe to store this way in local variables? We can do so very easily by making `proto::as_child()` behave just like `proto::as_expr()`. The following code achieves this:

    template< typename E >
    struct my_expr;

    struct my_generator
      : proto::pod_generator< my_expr >
    {};

    struct my_domain
      : proto::domain< my_generator >
    {
         // Make as_child() behave like as_expr() in my_domain.
         // (proto_base_domain is a typedef for proto::domain< my_generator >
         // that is defined in proto::domain<>.)
         template< typename T >
         struct as_child
           : proto_base_domain::as_expr< T >
         {};
    };

    template< typename E >
    struct my_expr
    {
        BOOST_PROTO_EXTENDS( E, my_expr< E >, my_domain )
    };

    /* ... */

    proto::literal< int, my_domain > i(0);
    auto l = i + 42; // OK! Everything is stored by value here.

Notice that `my_domain::as_child<>` simply defers to the default implementation of `as_expr<>` found in `proto::domain<>`. By simply cross-wiring our domain's `as_child<>` to `as_expr<>`, we guarantee that all terminals that can be held by value are, and that all child expressions are also held by value. This increases copying and may incur a runtime performance cost, but it eliminates any spector of lifetime management issues.

For another example, see the definition of `lldomain` in `libs/proto/example/lambda.hpp`. That example is a complete reimplementation of the Boost Lambda Library (BLL) on top of Boost.Proto. The function objects the BLL generates are safe to be stored in local variables. To emulate this with Proto, the `lldomain` cross-wires `as_child<>` to `as_expr<>` as above, but with one extra twist: objects with array type are also stored by reference. Check it out.

[2] It's not always possible to hold something by value. By default, `proto::as_expr()` makes an exception for functions, abstract types, and iostreams (types derived from `std::ios_base`). These objects are held by reference. All others are held by value, even arrays.