Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r79485 - in trunk: boost/proto/transform/detail boost/proto/transform/detail/preprocessed libs/proto/doc libs/proto/doc/reference/transform libs/proto/test
From: eric_at_[hidden]
Date: 2012-07-13 18:13:35


Author: eric_niebler
Date: 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
New Revision: 79485
URL: http://svn.boost.org/trac/boost/changeset/79485

Log:
user docs for unpacking patterns in transforms
Text files modified:
   trunk/boost/proto/transform/detail/pack_impl.hpp | 2
   trunk/boost/proto/transform/detail/preprocessed/call.hpp | 20 ++++----
   trunk/boost/proto/transform/detail/preprocessed/pack_impl.hpp | 20 ++++----
   trunk/libs/proto/doc/back_end.qbk | 96 ++++++++++++++++++++++++++++++++++++++++
   trunk/libs/proto/doc/proto.qbk | 2
   trunk/libs/proto/doc/reference/transform/impl.xml | 21 +++++---
   trunk/libs/proto/test/pack_expansion.cpp | 63 +++++++++++++++----------
   7 files changed, 170 insertions(+), 54 deletions(-)

Modified: trunk/boost/proto/transform/detail/pack_impl.hpp
==============================================================================
--- trunk/boost/proto/transform/detail/pack_impl.hpp (original)
+++ trunk/boost/proto/transform/detail/pack_impl.hpp 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
@@ -40,7 +40,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };

Modified: trunk/boost/proto/transform/detail/preprocessed/call.hpp
==============================================================================
--- trunk/boost/proto/transform/detail/preprocessed/call.hpp (original)
+++ trunk/boost/proto/transform/detail/preprocessed/call.hpp 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
@@ -15,7 +15,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A0
                   , detail::expand_pattern_rest_0<
                         Fun
@@ -35,7 +35,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A1
                   , detail::expand_pattern_rest_1<
                         Fun
@@ -55,7 +55,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A2
                   , detail::expand_pattern_rest_2<
                         Fun
@@ -106,7 +106,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A3
                   , detail::expand_pattern_rest_3<
                         Fun
@@ -157,7 +157,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A4
                   , detail::expand_pattern_rest_4<
                         Fun
@@ -208,7 +208,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A5
                   , detail::expand_pattern_rest_5<
                         Fun
@@ -259,7 +259,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A6
                   , detail::expand_pattern_rest_6<
                         Fun
@@ -310,7 +310,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A7
                   , detail::expand_pattern_rest_7<
                         Fun
@@ -361,7 +361,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A8
                   , detail::expand_pattern_rest_8<
                         Fun
@@ -412,7 +412,7 @@
         struct impl
           : call<
                 typename detail::expand_pattern<
- proto::arity_of<Expr>::value
+ proto::arity_of<Expr>::value
                   , A9
                   , detail::expand_pattern_rest_9<
                         Fun

Modified: trunk/boost/proto/transform/detail/preprocessed/pack_impl.hpp
==============================================================================
--- trunk/boost/proto/transform/detail/preprocessed/pack_impl.hpp (original)
+++ trunk/boost/proto/transform/detail/preprocessed/pack_impl.hpp 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
@@ -11,7 +11,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -77,7 +77,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -138,7 +138,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -194,7 +194,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -245,7 +245,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -291,7 +291,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -332,7 +332,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -368,7 +368,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -399,7 +399,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };
@@ -425,7 +425,7 @@
         {
             BOOST_MPL_ASSERT_MSG(
                 (expand_pattern_helper<proto::_child_c<0>, Fun>::applied::value)
- , NO_PACK_EXPRESSION_FOUND_IN_PACK_EXPANSION
+ , NO_PACK_EXPRESSION_FOUND_IN_UNPACKING_PATTERN
               , (Fun)
             );
         };

Modified: trunk/libs/proto/doc/back_end.qbk
==============================================================================
--- trunk/libs/proto/doc/back_end.qbk (original)
+++ trunk/libs/proto/doc/back_end.qbk 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
@@ -1158,6 +1158,102 @@
 
 [endsect]
 
+[/==================================================]
+[section:unpacking_expressions Unpacking Expressions]
+[/==================================================]
+
+Processing expressions with an arbitrary number of children can be a pain. What if you want to do something to each child, then pass the results as arguments to some other function? Can you do it just once without worrying about how many children an expression has? Yes. This is where Proto's /unpacking expressions/ come in handy. Unpacking expressions give you a way to write callable and object transforms that handle ['n]-ary expressions.
+
+[note *Inspired by C++11 Variadic Templates*
+
+Proto's unpacking expressions take inspiration from the C++11 feature of the same name. If you are familiar with variadic functions, and in particular how to expand a function parameter pack, this discussion should seem very familiar. However, this feature doesn't actually use any C++11 features, so the code describe here will work with any compliant C++98 compiler.]
+
+[heading Example: A C++ Expression Evaluator]
+
+Proto has the built-in _default_pt_ transform for evaluating Proto expressions in a C++-ish way. But if it didn't, it wouldn't be too hard to implement one from scratch using Proto's unpacking patterns. The transform `eval` below does just that.
+
+
+ template<typename T> T declval();
+
+ // A callable polymorphic function object that takes an unpacked expression
+ // and a tag, and evaluates the expression. A plus tag and two operands adds
+ // them with operator +, for instance.
+ struct do_eval : proto::callable
+ {
+ typedef double result_type;
+
+ #define UNARY_OP(TAG, OP) \
+ template<typename Arg> \
+ double operator()(proto::tag::TAG, Arg arg) const \
+ { \
+ return OP arg; \
+ } \
+ /**/
+
+ #define BINARY_OP(TAG, OP) \
+ template<typename Left, typename Right> \
+ double operator()(proto::tag::TAG, Left left, Right right) const \
+ { \
+ return left OP right; \
+ } \
+ /**/
+
+ UNARY_OP(negate, -)
+ BINARY_OP(plus, +)
+ BINARY_OP(minus, -)
+ BINARY_OP(multiplies, *)
+ BINARY_OP(divides, /)
+ /*... others ...*/
+ };
+
+ struct eval
+ : proto::or_<
+ // Evaluate terminals by simply returning their value
+ proto::when<proto::terminal<_>, proto::_value>
+
+ // Non-terminals are handled by unpacking the expression,
+ // recursively calling eval on each child, and passing
+ // the results along with the expression's tag to do_eval
+ // defined above.
+ , proto::otherwise<do_eval(proto::tag_of<_>(), eval(proto::pack(_))...)>
+ // UNPACKING PATTERN HERE -------------------^^^^^^^^^^^^^^^^^^^^^^^^
+ >
+ {};
+
+The bulk of the above code is devoted to the `do_eval` function object that maps tag types to behaviors, but the interesting bit is the definition of the `eval` algorithm at the bottom. Terminals are handled quite simply, but non-terminals could be unary, binary, ternary, even ['n]-ary if we consider function call expressions. The `eval` algorithm handles this uniformly with the help of an unpacking pattern.
+
+Non-terminals are evaluated with this callable transform:
+
+ do_eval(proto::tag_of<_>(), eval(proto::pack(_))...)
+
+You can read this as: call the `do_eval` function object with the tag of the current expression and all its children after they have each been evaluated with `eval`. The unpacking pattern is the bit just before the ellipsis: `eval(proto::pack(_))`.
+
+What's going on here is this. The unpacking expression gets repeated once for each child in the expression currently being evaluated. In each repetition, the type `proto::pack(_)` gets replaced with [^proto::_child_c<['N]>]. So, if a unary expression is passed to `eval`, it actually gets evaluated like this:
+
+ // After the unpacking pattern is expanded for a unary expression
+ do_eval(proto::tag_of<_>(), eval(proto::_child_c<0>))
+
+And when passed a binary expression, the unpacking pattern expands like this:
+
+ // After the unpacking pattern is expanded for a binary expression
+ do_eval(proto::tag_of<_>(), eval(proto::_child_c<0>), eval(proto::_child_c<1>))
+
+Although it can't happen in our example, when passed a terminal, the unpacking pattern expands such that it extracts the value from the terminal instead of the children. So it gets handled like this:
+
+ // If a terminal were passed to this transform, Proto would try
+ // to evaluate it like this, which would fail:
+ do_eval(proto::tag_of<_>(), eval(proto::_value))
+
+That doesn't make sense. `proto::_value` would return something that isn't a Proto expression, and `eval` wouldn't be able to evaluate it. Proto algorithms don't work unless you pass them Proto expressions.
+
+[note *Kickin' It Old School*
+
+You may be thinking, my compiler doesn't support C++11 variadic templates! How can this possibly work? The answer is simple: The `...` above isn't a C++11 pack expansion. It's actually an old-school C-style vararg. Remember that callable and object transforms are /function types/. A transform with one of these pseudo-pack expansions is really just the type of a boring, old vararg function. Proto just interprets it differently.]
+
+Unpacking patterns are very expressive. Any callable or object transform can be used as an unpacking pattern, so long as `proto::pack(_)` appears exactly once somewhere within it. This gives you a lot of flexibility in how you want to process the children of an expression before passing them on to some function object or object constructor.
+
+[endsect]
+
 [/=============================================================]
 [section:external_transforms Separating Grammars And Transforms]
 [/=============================================================]

Modified: trunk/libs/proto/doc/proto.qbk
==============================================================================
--- trunk/libs/proto/doc/proto.qbk (original)
+++ trunk/libs/proto/doc/proto.qbk 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
@@ -179,6 +179,8 @@
   [classref boost::proto::deduce_domain `proto::deduce_domain`]]
 [def _lazy_pt_
   [classref boost::proto::lazy `proto::lazy<>`]]
+[def _pack_
+ [classref boost::proto::pack `proto::pack`]]
 [def _SYB_
   [link boost_proto.users_guide.resources.SYB ["Scrap Your Boilerplate]]]
 [def _result_of_value_

Modified: trunk/libs/proto/doc/reference/transform/impl.xml
==============================================================================
--- trunk/libs/proto/doc/reference/transform/impl.xml (original)
+++ trunk/libs/proto/doc/reference/transform/impl.xml 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
@@ -186,12 +186,10 @@
 
       <!-- proto::pack -->
       <struct name="pack">
-
         <purpose>To turn an expression into a pseudo-parameter pack containing the
         expression's children, for the purpose of expanding the pack expression within
         a <conceptname>CallableTransform</conceptname> or
         <conceptname>ObjectTransform</conceptname>.</purpose>
-
         <description>
           <para>
             <computeroutput>proto::pack</computeroutput> is useful within
@@ -249,6 +247,14 @@
           </para>
 
           <para>
+ In the above example, the type
+ <computeroutput>
+ <classname alt="boost::proto::_value">proto::_value</classname>(proto::pack(<classname alt="boost::proto::_">_</classname>))
+ </computeroutput>
+ is a so-called <emphasis>unpacking pattern</emphasis>, described below.
+ </para>
+
+ <para>
             <emphasis role="bold">Unpacking Patterns:</emphasis>
           </para>
 
@@ -259,15 +265,15 @@
             However, when the argument list in a composite transform is terminated with a C-style
             vararg ellipsis as in <computeroutput>X(A<subscript>0</subscript>,…A<subscript>n</subscript> ...)</computeroutput>,
             the final argument <computeroutput>A<subscript>n</subscript></computeroutput> is treated
- as an <emphasis>unpacking expression</emphasis>.
+ as an <emphasis>unpacking pattern</emphasis>.
           </para>
           
           <para>
- An unpacking expression must itself be a composite transform; that is, it must be a
+ An unpacking pattern must itself be a composite transform; that is, it must be a
             function type representing either a <conceptname>CallableTransform</conceptname> or
             an <conceptname>ObjectTransform</conceptname>. The type <computeroutput>proto::pack(_)</computeroutput>
- must appear exactly once in the unpacking expression. This type will receive a substitution
- when the unpacking expression is expanded.
+ must appear exactly once in the unpacking pattern. This type will receive a substitution
+ when the unpacking pattern is expanded.
           </para>
 
           <para>
@@ -293,8 +299,7 @@
             </itemizedlist>
           </para>
         </description>
-
- </struct>
+ </struct>
       
     </namespace>
   </namespace>

Modified: trunk/libs/proto/test/pack_expansion.cpp
==============================================================================
--- trunk/libs/proto/test/pack_expansion.cpp (original)
+++ trunk/libs/proto/test/pack_expansion.cpp 2012-07-13 18:13:34 EDT (Fri, 13 Jul 2012)
@@ -20,31 +20,44 @@
     template<typename Sig>
     struct result;
 
- template<typename This, typename Left, typename Right>
- struct result<This(proto::tag::plus, Left, Right)>
- {
- typedef BOOST_TYPEOF_TPL(declval<Left>() + declval<Right>()) type;
- };
-
- template<typename This, typename Left, typename Right>
- struct result<This(proto::tag::multiplies, Left, Right)>
- {
- typedef BOOST_TYPEOF_TPL(declval<Left>() * declval<Right>()) type;
- };
-
- template<typename Left, typename Right>
- typename result<eval_(proto::tag::plus, Left, Right)>::type
- operator()(proto::tag::plus, Left left, Right right) const
- {
- return left + right;
- }
-
- template<typename Left, typename Right>
- typename result<eval_(proto::tag::multiplies, Left, Right)>::type
- operator()(proto::tag::multiplies, Left left, Right right) const
- {
- return left * right;
- }
+#define UNARY_OP(TAG, OP) \
+ template<typename This, typename Arg> \
+ struct result<This(proto::tag::TAG, Arg)> \
+ { \
+ BOOST_TYPEOF_NESTED_TYPEDEF_TPL(nested, (OP declval<Arg>())) \
+ typedef typename nested::type type; \
+ }; \
+ \
+ template<typename Arg> \
+ typename result<eval_(proto::tag::TAG, Arg)>::type \
+ operator()(proto::tag::TAG, Arg arg) const \
+ { \
+ return OP arg; \
+ } \
+ /**/
+
+#define BINARY_OP(TAG, OP) \
+ template<typename This, typename Left, typename Right> \
+ struct result<This(proto::tag::TAG, Left, Right)> \
+ { \
+ BOOST_TYPEOF_NESTED_TYPEDEF_TPL(nested, (declval<Left>() OP declval<Right>())) \
+ typedef typename nested::type type; \
+ }; \
+ \
+ template<typename Left, typename Right> \
+ typename result<eval_(proto::tag::TAG, Left, Right)>::type \
+ operator()(proto::tag::TAG, Left left, Right right) const \
+ { \
+ return left OP right; \
+ } \
+ /**/
+
+ UNARY_OP(negate, -)
+ BINARY_OP(plus, +)
+ BINARY_OP(minus, -)
+ BINARY_OP(multiplies, *)
+ BINARY_OP(divides, /)
+ /*... others ...*/
 };
 
 struct eval1


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk