|
Boost-Commit : |
From: eric_at_[hidden]
Date: 2008-08-10 22:07:01
Author: eric_niebler
Date: 2008-08-10 22:07:01 EDT (Sun, 10 Aug 2008)
New Revision: 48074
URL: http://svn.boost.org/trac/boost/changeset/48074
Log:
more doc tweaks
Text files modified:
branches/proto/v4/libs/proto/doc/calculator.qbk | 3
branches/proto/v4/libs/proto/doc/evaluation.qbk | 76 +++++---------
branches/proto/v4/libs/proto/doc/grammars.qbk | 203 +++++++++++++++++++--------------------
3 files changed, 127 insertions(+), 155 deletions(-)
Modified: branches/proto/v4/libs/proto/doc/calculator.qbk
==============================================================================
--- branches/proto/v4/libs/proto/doc/calculator.qbk (original)
+++ branches/proto/v4/libs/proto/doc/calculator.qbk 2008-08-10 22:07:01 EDT (Sun, 10 Aug 2008)
@@ -261,6 +261,9 @@
users of your DSEL short and readable compile-time errors when they accidentally
misuse your DSEL.
+[note `BOOST_MPL_ASSERT()` is part of the Boost Metaprogramming Library. To use it,
+just `#include <boost/mpl/assert.hpp>`.]
+
[/=====================================]
[heading Controlling Operator Overloads]
[/=====================================]
Modified: branches/proto/v4/libs/proto/doc/evaluation.qbk
==============================================================================
--- branches/proto/v4/libs/proto/doc/evaluation.qbk (original)
+++ branches/proto/v4/libs/proto/doc/evaluation.qbk 2008-08-10 22:07:01 EDT (Sun, 10 Aug 2008)
@@ -124,7 +124,7 @@
// A nested eval<> class template
template<
typename Expr
- , typename Tag = typename tag_of<Expr>::type
+ , typename Tag = typename proto::tag_of<Expr>::type
>
struct eval;
@@ -160,45 +160,28 @@
// implemented from scratch.
struct calculator_context
{
- // The values for the _1 and _2 placeholders are
- // passed to the calculator_context constructor.
- calculator_context(double d1, double d2)
- : d1_(d1), d2_(d2)
- {}
+ // The values with which we'll replace the placeholders
+ std::vector<double> args;
template<
typename Expr
// defaulted template parameters, so we can
// specialize on the expressions that need
// special handling.
- , typename Tag = typename tag_of<Expr>::type
- , typename Arg0 = typename child_c<Expr, 0>::type
+ , typename Tag = typename proto::tag_of<Expr>::type
+ , typename Arg0 = typename proto::child_c<Expr, 0>::type
>
struct eval;
// Handle placeholder1 terminals here...
- template<typename Expr>
- struct eval<Expr, proto::tag::terminal, placeholder1>
- {
- typedef double result_type;
-
- result_type operator()(Expr &, MyContext &ctx) const
- {
- // replaces _1 with the value in ctx.d1_
- return ctx.d1_;
- }
- };
-
- // Handle placeholder2 terminals here...
- template<typename Expr>
- struct eval<Expr, proto::tag::terminal, placeholder2>
+ template<typename Expr, int I>
+ struct eval<Expr, proto::tag::terminal, placeholder<I> >
{
typedef double result_type;
result_type operator()(Expr &, MyContext &ctx) const
{
- // replaces _1 with the value in ctx.d2_
- return ctx.d2_;
+ return ctx.args[I];
}
};
@@ -228,15 +211,16 @@
};
// ... other eval<> specializations for other node types ...
-
- double d1_, d2_;
};
Now we can use _eval_ with the context class above to evaluate calculator
expressions as follows:
// Evaluate an expression with a calculator_context
- double d = proto::eval(_1 + _2, calculator_context(5, 6));
+ calculator_context ctx;
+ ctx.args.push_back(5);
+ ctx.args.push_back(6);
+ double d = proto::eval(_1 + _2, ctx);
assert(11 == d);
Defining a context from scratch this way is tedious and verbose, but it gives
@@ -516,26 +500,17 @@
struct calculator_context
: proto::callable_context< calculator_context const >
{
- calculator_context(double d1, double d2)
- : d1_(d1), d2_(d2)
- {}
+ std::vector<double> args;
// Define the result type of the calculator.
typedef double result_type;
// Handle the placeholders:
- double operator()(proto::tag::terminal, placeholder1) const
+ template<int I>
+ double operator()(proto::tag::terminal, placeholder<I>) const
{
- return this->d1_;
+ return this->args[I];
}
-
- double operator()(proto::tag::terminal, placeholder2) const
- {
- return this->d2_;
- }
-
- private:
- double d1_, d2_;
};
In this case, we didn't specify a fall-back context. In that case,
@@ -544,16 +519,19 @@
terminals, we can evaluate calculator expressions, as demonstrated
below:
- struct placeholder1 {};
- struct placeholder2 {};
- terminal<placeholder1>::type const _1 = {{}};
- terminal<placeholder2>::type const _2 = {{}};
+ template<int I>
+ struct placeholder
+ {};
+
+ terminal<placeholder<0> >::type const _1 = {{}};
+ terminal<placeholder<1> >::type const _2 = {{}};
// ...
- double j = proto::eval(
- (_2 - _1) / _2 * 100
- , calculator_context(4, 5)
- );
+ calculator_context ctx;
+ ctx.args.push_back(4);
+ ctx.args.push_back(5);
+
+ double j = proto::eval( (_2 - _1) / _2 * 100, ctx );
std::cout << "j = " << j << std::endl;
The above code displays the following:
Modified: branches/proto/v4/libs/proto/doc/grammars.qbk
==============================================================================
--- branches/proto/v4/libs/proto/doc/grammars.qbk (original)
+++ branches/proto/v4/libs/proto/doc/grammars.qbk 2008-08-10 22:07:01 EDT (Sun, 10 Aug 2008)
@@ -30,24 +30,24 @@
First, let's define some terminals we can use in our lazy I/O expressions:
- terminal< std::istream & >::type cin_ = { std::cin };
- terminal< std::ostream & >::type cout_ = { std::cout };
+ proto::terminal< std::istream & >::type cin_ = { std::cin };
+ proto::terminal< std::ostream & >::type cout_ = { std::cout };
Now, we can use `cout_` instead of `std::cout`, and get I/O expression trees
that we can execute later. To define grammars that match intput and output
expressions of the form `cin_ >> i` and `cout_ << 1` we do this:
struct Input
- : shift_right< terminal< std::istream & >, _ >
+ : proto::shift_right< proto::terminal< std::istream & >, proto::_ >
{};
struct Output
- : shift_left< terminal< std::ostream & >, _ >
+ : proto::shift_left< proto::terminal< std::ostream & >, proto::_ >
{};
-We've seen the template `terminal<>` before, but here we're using it
+We've seen the template `proto::terminal<>` before, but here we're using it
without accessing the nested `::type`. When used like this, it is a very simple
-grammar, as are `shift_right<>` and `shift_left<>`. The newcomer
+grammar, as are `proto::shift_right<>` and `proto::shift_left<>`. The newcomer
here is `_` in the `proto` namespace. It is a wildcard that matches anything.
The `Input` struct is a grammar that matches any right-shift expression that
has a `std::istream` terminal as its left operand.
@@ -59,12 +59,12 @@
template< typename Expr >
void input_output( Expr const & expr )
{
- if( matches< Expr, Input >::value )
+ if( proto::matches< Expr, Input >::value )
{
std::cout << "Input!\n";
}
- if( matches< Expr, Output >::value )
+ if( proto::matches< Expr, Output >::value )
{
std::cout << "Output!\n";
}
@@ -91,14 +91,14 @@
`boost::enable_if<>`, as follows:
template< typename Expr >
- typename enable_if< matches< Expr, Input > >::type
+ typename boost::enable_if< proto::matches< Expr, Input > >::type
input_output( Expr const & expr )
{
std::cout << "Input!\n";
}
template< typename Expr >
- typename enable_if< matches< Expr, Output > >::type
+ typename boost::enable_if< proto::matches< Expr, Output > >::type
input_output( Expr const & expr )
{
std::cout << "Output!\n";
@@ -121,16 +121,16 @@
`Input` and `Output` grammars:
struct Input
- : or_<
- shift_right< terminal< std::istream & >, _ >
- , shift_right< Input, _ >
+ : proto::or_<
+ proto::shift_right< proto::terminal< std::istream & >, proto::_ >
+ , proto::shift_right< Input, proto::_ >
>
{};
struct Output
- : or_<
- shift_left< terminal< std::ostream & >, _ >
- , shift_left< Output, _ >
+ : proto::or_<
+ proto::shift_left< proto::terminal< std::ostream & >, proto::_ >
+ , proto::shift_left< Output, proto::_ >
>
{};
@@ -142,16 +142,15 @@
rightly so because they must match a recursive data structure!
When the `Output` grammar is evaluated against an expression like
-`cout_ << 1 << 2`, the first alternate of the _or_ is tried first. It will
-fail, because the expression `cout_ << 1 << 2` does not match the grammar
-`shift_left< terminal< std::ostream & >, _ >`. Then the second
+`cout_ << 1 << 2`, the first alternate of the _or_ is tried first. It will fail,
+because the expression `cout_ << 1 << 2` does not match the grammar
+`proto::shift_left< proto::terminal< std::ostream & >, proto::_ >`. Then the second
alternate is tried. We match the expression against
-`shift_left< Output, _ >`. The expression is a left-shift, so we try
-the operands. The right operand `2` matches `_` trivially. To see if
-the left operand `cout_ << 1` matches `Output`, we must recursively evaluate
-the `Output` grammar. This time we succeed, because `cout_ << 1` will match
-the first alternate of the _or_. We're done -- the grammar matches
-successfully.
+`proto::shift_left< Output, proto::_ >`. The expression is a left-shift, so we try
+the operands. The right operand `2` matches `proto::_` trivially. To see if the
+left operand `cout_ << 1` matches `Output`, we must recursively evaluate the
+`Output` grammar. This time we succeed, because `cout_ << 1` will match the first
+alternate of the _or_. We're done -- the grammar matches successfully.
[endsect]
@@ -189,8 +188,8 @@
This begs the question: What if you want to match an `int`, but not an `int &`
or an `int const &`? For forcing exact matches, Proto provides the _exact_
-template. For instance, `proto::terminal<exact<int> >` would only match an `int`
-held by value.
+template. For instance, `proto::terminal< proto::exact<int> >` would only match an
+`int` held by value.
Proto gives you extra wiggle room when matching array types. Array types match
themselves or the pointer types they decay to. This is especially useful with
@@ -202,42 +201,42 @@
code fragment illustrates.
struct CharString
- : terminal< char const * >
+ : proto::terminal< char const * >
{};
- typedef terminal< char const[6] >::type char_array;
+ typedef proto::terminal< char const[6] >::type char_array;
- BOOST_MPL_ASSERT(( matches< char_array, CharString > ));
+ BOOST_MPL_ASSERT(( proto::matches< char_array, CharString > ));
What if we only wanted `CharString` to match terminals of exactly the type
`char const *`? You can use _exact_ here to turn off the fuzzy matching of
terminals, as follows:
struct CharString
- : terminal< exact< char const * > >
+ : proto::terminal< proto::exact< char const * > >
{};
- typedef terminal<char const[6]>::type char_array;
- typedef terminal<char const *>::type char_string;
+ typedef proto::terminal<char const[6]>::type char_array;
+ typedef proto::terminal<char const *>::type char_string;
- BOOST_MPL_ASSERT(( matches< char_string, CharString > ));
- BOOST_MPL_ASSERT_NOT(( matches< char_array, CharString > ));
+ BOOST_MPL_ASSERT(( proto::matches< char_string, CharString > ));
+ BOOST_MPL_ASSERT_NOT(( proto::matches< char_array, CharString > ));
Now, `CharString` does not match array types, only character string pointers.
The inverse problem is a little trickier: what if you wanted to match all
character arrays, but not character pointers? As mentioned above, the
expression `as_expr("hello")` has the type
-`terminal< char const[ 6 ] >::type`. If you wanted to match character
+`proto::terminal< char const[ 6 ] >::type`. If you wanted to match character
arrays of arbitrary size, you could use `proto::N`, which is an array-size
wildcard. The following grammar would match any string literal:
-`terminal< char const[ proto::N ] >`.
+`proto::terminal< char const[ proto::N ] >`.
Sometimes you need even more wiggle room when matching terminals. For
example, maybe you're building a calculator DSEL and you want to allow any
terminals that are convertible to `double`. For that, Proto provides the
_convertible_to_ template. You can use it as:
-`proto::terminal<proto::convertible_to<double> >`.
+`proto::terminal< proto::convertible_to< double > >`.
There is one more way you can perform a fuzzy match on terminals. Consider the
problem of trying to match a `std::complex<>` terminal. You can easily match
@@ -246,7 +245,7 @@
this problem. Here is the grammar to match any `std::complex<>` instantiation:
struct StdComplex
- : terminal< std::complex< _ > >
+ : proto::terminal< std::complex< proto::_ > >
{};
When given a grammar like this, Proto will deconstruct the grammar and the
@@ -259,8 +258,8 @@
[section:if_and_not [^if_<>], [^and_<>], and [^not_<>]]
[/====================================================]
-We've already seen how to use expression generators like `terminal<>` and
-`shift_right<>` as grammars. We've also seen _or_, which we can use to
+We've already seen how to use expression generators like `proto::terminal<>` and
+`proto::shift_right<>` as grammars. We've also seen _or_, which we can use to
express a set of alternate grammars. There are a few others of interest; in
particular, _if_, _and_ and _not_.
@@ -278,16 +277,16 @@
_exact_ as follows:
struct CharString
- : and_<
- terminal< _ >
- , if_< is_same< _value, char const * >() >
+ : proto::and_<
+ proto::terminal< proto::_ >
+ , proto::if_< boost::is_same< proto::_value, char const * >() >
>
{};
-This says that a `CharString` must be a terminal, /and/ its argument must be
+This says that a `CharString` must be a terminal, /and/ its value type must be
the same as `char const *`. Notice the template argument of _if_:
-`is_same< _value, char const * >()`. This is Proto transform that compares the
-value of a terminal to `char const *`.
+`boost::is_same< proto::_value, char const * >()`. This is Proto transform that
+compares the value of a terminal to `char const *`.
The _if_ template has a couple of variants. In additon to `if_<Condition>` you
can also say `if_<Condition, ThenGrammar>` and
@@ -305,7 +304,8 @@
of arguments. Likewise, with Proto you may define your own "operators" that
could also take more that two arguments. As a result, there may be nodes in
your Proto expression tree that have an arbitrary number of children (up to
-some predefined maximum). How do you write a grammar to match such a node?
+`BOOST_PROTO_MAX_ARITY`, which is configurable). How do you write a grammar to
+match such a node?
For such cases, Proto provides the _vararg_ class template. Its template
argument is a grammar, and the _vararg_ will match the grammar zero or more
@@ -313,7 +313,7 @@
more characters as arguments, as follows:
struct fun_tag {};
- struct FunTag : terminal< fun_tag > {};
+ struct FunTag : proto::terminal< fun_tag > {};
FunTag::type const fun = {{}};
// example usage:
@@ -325,7 +325,7 @@
Below is the grammar that matches all the allowable invocations of `fun()`:
struct FunCall
- : function< FunTag, vararg< terminal< char > > >
+ : proto::function< FunTag, proto::vararg< proto::terminal< char > > >
{};
The `FunCall` grammar uses _vararg_ to match zero or more character literals
@@ -334,16 +334,16 @@
As another example, can you guess what the following grammar matches?
struct Foo
- : or_<
- terminal< _ >
- , nary_expr< _, vararg< Foo > >
+ : proto::or_<
+ proto::terminal< proto::_ >
+ , proto::nary_expr< proto::_, proto::vararg< Foo > >
>
{};
-Here's a hint: the first template parameter to `nary_expr<>` represents the
-node type, and any additional template parameters represent child nodes. The
-answer is that this is a degenerate grammar that matches every possible
-expression tree, from root to leaves.
+Here's a hint: the first template parameter to `proto::nary_expr<>` represents the
+node type, and any additional template parameters represent child nodes. The answer
+is that this is a degenerate grammar that matches every possible expression tree,
+from root to leaves.
[endsect]
@@ -351,40 +351,32 @@
[section Defining DSEL Grammars]
[/=============================]
-We've already seen how to use small grammars to answer simple questions about
-expression trees. Here's a harder question: ["Does this expression conform to the
-grammar of my domain-specific embedded language?] In this section we'll see how
-to use Proto to define a grammar for your DSEL and use it to validate
-expression templates, giving short, readable compile-time errors for invalid
-expressions.
-
-[tip You might be thinking that this is a backwards way of doing things.
-["If Proto let me select which operators to overload, my users wouldn't be able
-to create invalid expressions in the first place, and I wouldn't need a grammar
-at all!] That may be true, but there are reasons for preferring to do things
-this way.
-
-First, it lets you develop your DSEL rapidly -- all the operators are
-there for you already! -- and worry about invalid syntax later.
-
-Second, it
-might be the case that some operators are only allowed in certain contexts
-within your DSEL. This is easy to express with a grammar, and hard to do with
-straight operator overloading.
-
-Third, using a DSEL grammar to flag invalid
-expressions can often yield better errors than manually selecting the
-overloaded operators.
-
-Fourth, the grammar can be used for more than just
-validation. As we'll see later, you can use your grammar to define ['tree
-transformations] that convert expression templates into other more useful
-objects.
-
-If none of the above convinces you, you actually /can/ use Proto to control
-which operators are overloaded within your domain. And to do it, you need to
-define a grammar! We'll see how later.
-]
+In this section we'll see how to use Proto to define a grammar for your DSEL and
+use it to validate expression templates, giving short, readable compile-time errors
+for invalid expressions.
+
+[tip You might think that this is a backwards way of doing things. ["If Proto let
+me select which operators to overload, my users wouldn't be able to create invalid
+expressions in the first place, and I wouldn't need a grammar at all!] That may be
+true, but there are reasons for preferring to do things this way.
+
+First, it lets you develop your DSEL rapidly -- all the operators are there for you
+already! -- and worry about invalid syntax later.
+
+Second, it might be the case that some operators are only allowed in certain
+contexts within your DSEL. This is easy to express with a grammar, and hard to do
+with straight operator overloading.
+
+Third, using a DSEL grammar to flag invalid expressions can often yield better
+errors than manually selecting the overloaded operators.
+
+Fourth, the grammar can be used for more than just validation. You can use your
+grammar to define ['tree transformations] that convert expression templates into
+other more useful objects.
+
+If none of the above convinces you, you actually /can/ use Proto to control which
+operators are overloaded within your domain. And to do it, you need to define a
+grammar!]
In a previous section, we used Proto to define a DSEL for a lazily evaluated
calculator that allowed any combination of placeholders, floating-point
@@ -415,48 +407,47 @@
define the rules of our grammar. Let's define grammar rules for the terminals:
struct Double
- : terminal< convertible_to< double > >
+ : proto::terminal< proto::convertible_to< double > >
{};
struct Placeholder1
- : terminal< placeholder1 >
+ : proto::terminal< placeholder<0> >
{};
struct Placeholder2
- : terminal< placeholder2 >
+ : proto::terminal< placeholder<1> >
{};
struct Terminal
- : or_< Double, Placeholder1, Placeholder2 >
+ : proto::or_< Double, Placeholder1, Placeholder2 >
{};
-Now let's define the rules for addition, subtraction, multiplication and
-division. Here, we can ignore issues of associativity and precedence -- the C++
-compiler will enforce that for us. We only must enforce that the arguments to
-the operators must themselves conform to the `CalculatorGrammar` that we
-forward-declared above.
+Now let's define the rules for addition, subtraction, multiplication and division.
+Here, we can ignore issues of associativity and precedence -- the C++ compiler will
+enforce that for us. We only must enforce that the arguments to the operators must
+themselves conform to the `CalculatorGrammar` that we forward-declared above.
struct Plus
- : plus< CalculatorGrammar, CalculatorGrammar >
+ : proto::plus< CalculatorGrammar, CalculatorGrammar >
{};
struct Minus
- : minus< CalculatorGrammar, CalculatorGrammar >
+ : proto::minus< CalculatorGrammar, CalculatorGrammar >
{};
struct Multiplies
- : multiplies< CalculatorGrammar, CalculatorGrammar >
+ : proto::multiplies< CalculatorGrammar, CalculatorGrammar >
{};
struct Divides
- : divides< CalculatorGrammar, CalculatorGrammar >
+ : proto::divides< CalculatorGrammar, CalculatorGrammar >
{};
Now that we've defined all the parts of the grammar, we can define
`CalculatorGrammar`:
struct CalculatorGrammar
- : or_<
+ : proto::or_<
Terminal
, Plus
, Minus
@@ -472,7 +463,7 @@
template< typename Expr >
void evaluate( Expr const & expr )
{
- BOOST_MPL_ASSERT(( matches< Expr, CalculatorGrammar > ));
+ BOOST_MPL_ASSERT(( proto::matches< Expr, CalculatorGrammar > ));
// ...
}
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