![]() |
Home | Libraries | People | FAQ | More |
After assembling an expression into a tree, you'll naturally want to be able to do the reverse, and access a node's children. You may even want to be able to iterate over the children with algorithms from the Boost.Fusion library. This section shows how.
Every node in an expression tree has both a tag type
that describes the node, and an arity corresponding
to the number of child nodes it has. You can use the proto::tag_of<>
and proto::arity_of<>
metafunctions to fetch
them. Consider the following:
template<typename Expr> void check_plus_node(Expr const &) { // Assert that the tag type is proto::tag::plus BOOST_STATIC_ASSERT(( boost::is_same< typename proto::tag_of<Expr>::type , proto::tag::plus >::value )); // Assert that the arity is 2 BOOST_STATIC_ASSERT( proto::arity_of<Expr>::value == 2 ); } // Create a binary plus node and use check_plus_node() // to verify its tag type and arity: check_plus_node( proto::lit(1) + 2 );
For a given type Expr
,
you could access the tag and arity directly as Expr::proto_tag
and Expr::proto_arity
, where Expr::proto_arity
is an MPL Integral Constant.
There is no simpler expression than a terminal, and no more basic operation
than extracting its value. As we've already seen, that is what proto::value()
is for.
proto::terminal< std::ostream & >::type cout_ = {std::cout}; // Get the value of the cout_ terminal: std::ostream & sout = proto::value( cout_ ); // Assert that we got back what we put in: assert( &sout == &std::cout );
To compute the return type of the proto::value()
function, you can use proto::result_of::value<>
.
When the parameter to proto::result_of::value<>
is a non-reference type, the result type of the metafunction is the type
of the value as suitable for storage by value; that is, top-level reference
and qualifiers are stripped from it. But when instantiated with a reference
type, the result type has a reference added to it,
yielding a type suitable for storage by reference. If you want to know
the actual type of the terminal's value including whether it is stored
by value or reference, you can use fusion::result_of::value_at<Expr, 0>::type
.
The following table summarizes the above paragraph.
Table 1.4. Accessing Value Types
Metafunction Invocation |
When the Value Type Is ... |
The Result Is ... |
---|---|---|
|
|
typename boost::remove_const< typename boost::remove_reference<T>::type >::type [a]
|
|
|
typename boost::add_reference<T>::type
|
|
|
typename boost::add_reference< typename boost::add_const<T>::type >::type
|
|
|
|
[a] If |
Each non-terminal node in an expression tree corresponds to an operator
in an expression, and the children correspond to the operands, or arguments
of the operator. To access them, you can use the proto::child_c()
function template, as demonstrated below:
proto::terminal<int>::type i = {42}; // Get the 0-th operand of an addition operation: proto::terminal<int>::type &ri = proto::child_c<0>( i + 2 ); // Assert that we got back what we put in: assert( &i == &ri );
You can use the proto::result_of::child_c<>
metafunction to get the type of the Nth child of an expression node. Usually
you don't care to know whether a child is stored by value or by reference,
so when you ask for the type of the Nth child of an expression Expr
(where Expr
is not a reference type), you get the child's type after references and
cv-qualifiers have been stripped from it.
template<typename Expr> void test_result_of_child_c(Expr const &expr) { typedef typename proto::result_of::child_c<Expr, 0>::type type; // Since Expr is not a reference type, // result_of::child_c<Expr, 0>::type is a // non-cv qualified, non-reference type: BOOST_MPL_ASSERT(( boost::is_same< type, proto::terminal<int>::type > )); } // ... proto::terminal<int>::type i = {42}; test_result_of_child_c( i + 2 );
However, if you ask for the type of the Nth child of Expr
&
or Expr
const &
(note the reference), the result type will be a reference, regardless of
whether the child is actually stored by reference or not. If you need to
know exactly how the child is stored in the node, whether by reference
or by value, you can use fusion::result_of::value_at<Expr, N>::type
. The following table summarizes
the behavior of the proto::result_of::child_c<>
metafunction.
Table 1.5. Accessing Child Types
Metafunction Invocation |
When the Child Is ... |
The Result Is ... |
---|---|---|
|
|
typename boost::remove_const< typename boost::remove_reference<T>::type >::type
|
|
|
typename boost::add_reference<T>::type
|
|
|
typename boost::add_reference< typename boost::add_const<T>::type >::type
|
|
|
|
Most operators in C++ are unary or binary, so accessing the only operand,
or the left and right operands, are very common operations. For this reason,
Proto provides the proto::child()
,
proto::left()
, and proto::right()
functions. proto::child()
and proto::left()
are synonymous with proto::child_c<0>()
,
and proto::right()
is synonymous with proto::child_c<1>()
.
There are also proto::result_of::child<>
,
proto::result_of::left<>
, and proto::result_of::right<>
metafunctions that merely forward to their proto::result_of::child_c<>
counterparts.