| Home | Libraries | People | FAQ | More

### Intermediate Form: Understanding and Introspecting Expressions

By now, you know a bit about how to build a front-end for your EDSL "compiler" -- you can define terminals and functions that generate expression templates. But we haven't said anything about the expression templates themselves. What do they look like? What can you do with them? In this section we'll see.

##### The `expr<>` Type

All Proto expressions are an instantiation of a template called `proto::expr<>` (or a wrapper around such an instantiation). When we define a terminal as below, we are really initializing an instance of the `proto::expr<>` template.

    // Define a placeholder type
    template<int I>
    struct placeholder
    {};

    // Define the Protofied placeholder terminal
    proto::terminal< placeholder<0> >::type const _1 = {{}};

The actual type of `_1` looks like this:

    proto::expr< proto::tag::terminal, proto::term< placeholder<0> >, 0 >

The `proto::expr<>` template is the most important type in Proto. Although you will rarely need to deal with it directly, it's always there behind the scenes holding your expression trees together. In fact, `proto::expr<>` is the expression tree -- branches, leaves and all.

The `proto::expr<>` template makes up the nodes in expression trees. The first template parameter is the node type; in this case, `proto::tag::terminal`. That means that `_1` is a leaf-node in the expression tree. The second template parameter is a list of child types, or in the case of terminals, the terminal's value type. Terminals will always have only one type in the type list. The last parameter is the arity of the expression. Terminals have arity 0, unary expressions have arity 1, etc.

The `proto::expr<>` struct is defined as follows:

    template< typename Tag, typename Args, long Arity = Args::arity >
    struct expr;

    template< typename Tag, typename Args >
    struct expr< Tag, Args, 1 >
    {
        typedef typename Args::child0 proto_child0;
        proto_child0 child0;
        // ...
    };

The `proto::expr<>` struct does not define a constructor, or anything else that would prevent static initialization. All `proto::expr<>` objects are initialized using aggregate initialization, with curly braces. In our example, `_1` is initialized with the initializer `{{}}`. The outer braces are the initializer for the `proto::expr<>` struct, and the inner braces are for the member `_1.child0` which is of type `placeholder<0>`. Note that we use braces to initialize `_1.child0` because `placeholder<0>` is also an aggregate.

##### Building Expression Trees

The `_1` node is an instantiation of `proto::expr<>`, and expressions containing `_1` are also instantiations of `proto::expr<>`. To use Proto effectively, you won't have to bother yourself with the actual types that Proto generates. These are details, but you're likely to encounter these types in compiler error messages, so it's helpful to be familiar with them. The types look like this:

    // The type of the expression -_1
    typedef
        proto::expr<
            proto::tag::negate
          , proto::list1<
                proto::expr<
                    proto::tag::terminal
                  , proto::term< placeholder<0> >
                  , 0
                > const &
            >
          , 1
        >
    negate_placeholder_type;

    negate_placeholder_type x = -_1;

    // The type of the expression _1 + 42
    typedef
        proto::expr<
            proto::tag::plus
          , proto::list2<
                proto::expr<
                    proto::tag::terminal
                  , proto::term< placeholder<0> >
                  , 0
                > const &
              , proto::expr<
                    proto::tag::terminal
                  , proto::term< int const & >
                  , 0
                >
            >
          , 2
        >
    placeholder_plus_int_type;

    placeholder_plus_int_type y = _1 + 42;

There are a few things to note about these types:

- Terminals have arity zero, unary expressions have arity one and binary expressions have arity two.
- When one Proto expression is made a child node of another Proto expression, it is held by reference, even if it is a temporary object. This last point becomes important later.
- Non-Proto expressions, such as the integer literal, are turned into Proto expressions by wrapping them in new `expr<>` terminal objects. These new wrappers are not themselves held by reference, but the object wrapped is. Notice that the type of the Protofied `42` literal is `int const &` -- held by reference.

The types make it clear: everything in a Proto expression tree is held by reference. That means that building an expression tree is exceptionally cheap. It involves no copying at all.

| Note

An astute reader will notice that the object `y` defined above will be left holding a dangling reference to a temporary int. In the sorts of high-performance applications Proto addresses, it is typical to build and evaluate an expression tree before any temporary objects go out of scope, so this dangling reference situation often doesn't arise, but it is certainly something to be aware of. Proto provides utilities for deep-copying expression trees so they can be passed around as value types without concern for dangling references.