![]() |
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.3. Accessing Value Types
|
Metafunction Invocation |
When the Value Type Is ... |
The Result Is ... |
|---|---|---|
|
proto::result_of::value<Expr>::type |
T |
typename boost::remove_const< typename boost::remove_reference<T>::type >::type [a]
|
|
proto::result_of::value<Expr &>::type |
T |
typename boost::add_reference<T>::type
|
|
proto::result_of::value<Expr const &>::type |
T |
typename boost::add_reference< typename boost::add_const<T>::type >::type
|
|
fusion::result_of::value_at<Expr, 0>::type |
T |
T |
[a] If T is a reference-to-function type, then the result type is simply T. | ||
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.4. Accessing Child Types
|
Metafunction Invocation |
When the Child Is ... |
The Result Is ... |
|---|---|---|
|
proto::result_of::child_c<Expr, N>::type |
T |
typename boost::remove_const< typename boost::remove_reference<T>::type >::type
|
|
proto::result_of::child_c<Expr &, N>::type |
T |
typename boost::add_reference<T>::type
|
|
proto::result_of::child_c<Expr const &, N>::type |
T |
typename boost::add_reference< typename boost::add_const<T>::type >::type
|
|
fusion::result_of::value_at<Expr, N>::type |
T |
T |
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.