back_end.qbk

来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 1,356 行 · 第 1/5 页

QBK
1,356
字号
[/ / Copyright (c) 2007 Eric Niebler / / Distributed under the Boost Software License, Version 1.0. (See accompanying / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /][/================================================================================][section:back_end Back Ends:    Making Expression Templates Do Useful Work][/================================================================================]Now that you've written the front end for your DSEL compiler, and you've learned a bit about the intermediate form it produces, it's time to think about what to /do/ with the intermediate form. This is where you put your domain-specific algorithms and optimizations. Proto gives you two ways to evaluate and manipulate expression templates: contexts and transforms.* A /context/ is like a function object that you pass along with an expression to   the _eval_ function. It associates behaviors with node types. _eval_ walks the   expression and invokes your context at each node.* A /transform/ is a way to associate behaviors, not with node types in an   expression, but with rules in a Proto grammar. In this way, they are like   semantic actions in other compiler-construction toolkits.Two ways to evaluate expressions! How to choose? Contexts are a bit simpler to understand and to debug, since they are largely procedural, so contexts are a good place to start. But although transforms are more advanced, they are also more powerful; since they are associated with rules in your grammar, you can select the proper transform based on the entire /structure/ of a sub-expression rather than simply on the type of its top-most node.Also, transforms have a concise and declarative syntax that can be confusing at first, but highly expressive and fungible once you become accustomed to it. And -- this is admittedly very subjective -- the author finds programming with Proto transforms to be an inordinate amount of /fun!/ Your mileage may vary.[/================================================================================][section:expression_evaluation Expression Evaluation:    Imparting Behaviors with a Context][/================================================================================]Once you have constructed a Proto expression tree, either by using Proto'soperator overloads or with _make_expr_ and friends, you probably want toactually /do/ something with it. The simplest option is to use `proto::eval()`,a generic expression evaluator. To use _eval_, you'll need to define a/context/ that tells _eval_ how each node should be evaluated. This sectiongoes through the nuts and bolts of using _eval_, defining evaluation contexts,and using the contexts that Proto provides.[note `proto::eval()` is a less powerful but easier-to-use evaluation techniquethan Proto transforms, which are covered later. Although very powerful,transforms have a steep learning curve and can be more difficult to debug.`proto::eval()` is a rather weak tree traversal algorithm. Dan Marsden hasbeen working on a more general and powerful tree traversal library. When it isready, I anticipate that it will eliminate the need for `proto::eval()`.][/================================================================][section:proto_eval Evaluating an Expression with [^proto::eval()]][/================================================================][:[*Synopsis:]]    namespace proto    {        namespace result_of        {            // A metafunction for calculating the return            // type of proto::eval() given certain Expr            // and Context types.            template<typename Expr, typename Context>            struct eval            {                typedef                    typename Context::template eval<Expr>::result_type                type;            };        }        namespace functional        {            // A callable function object type for evaluating            // a Proto expression with a certain context.            struct eval : callable            {                template<typename Sig>                struct result;                template<typename Expr, typename Context>                typename proto::result_of::eval<Expr, Context>::type                operator ()(Expr &expr, Context &context) const;                template<typename Expr, typename Context>                typename proto::result_of::eval<Expr, Context>::type                operator ()(Expr &expr, Context const &context) const;            };        }        template<typename Expr, typename Context>        typename proto::result_of::eval<Expr, Context>::type        eval(Expr &expr, Context &context);        template<typename Expr, typename Context>        typename proto::result_of::eval<Expr, Context>::type        eval(Expr &expr, Context const &context);    }Given an expression and an evaluation context, using _eval_ is quite simple.Simply pass the expression and the context to _eval_ and it does the rest andreturns the result. You can use the `eval<>` metafunction in the`proto::result_of` namespace to compute the return type of _eval_. Thefollowing demonstrates a use of _eval_:    template<typename Expr>    typename proto::result_of::eval<Expr const, MyContext>::type    MyEvaluate(Expr const &expr)    {        // Some user-defined context type        MyContext ctx;        // Evaluate an expression with the context        return proto::eval(expr, ctx);    }What _eval_ does is also very simple. It defers most of the work to thecontext itself. Here essentially is the implementation of _eval_:    // eval() dispatches to a nested "eval<>" function    // object within the Context:    template<typename Expr, typename Context>    typename Context::template eval<Expr>::result_type    eval(Expr &expr, Context &ctx)    {        typename Context::template eval<Expr> eval_fun;        return eval_fun(expr, ctx);    }Really, _eval_ is nothing more than a thin wrapper that dispatches to theappropriate handler within the context class. In the next section, we'll seehow to implement a context class from scratch.[endsect][/==============================================][section:contexts Defining an Evaluation Context][/==============================================]As we saw in the previous section, there is really not much to the _eval_function. Rather, all the interesting expression evaluation goes on withina context class. This section shows how to implement one from scratch.All context classes have roughly the following form:    // A prototypical user-defined context.    struct MyContext    {        // A nested eval<> class template        template<            typename Expr          , typename Tag = typename proto::tag_of<Expr>::type        >        struct eval;        // Handle terminal nodes here...        template<typename Expr>        struct eval<Expr, proto::tag::terminal>        {            // Must have a nested result_type typedef.            typedef ... result_type;            // Must have a function call operator that takes            // an expression and the context.            result_type operator()(Expr &expr, MyContext &ctx) const            {                return ...;            }        };        // ... other specializations of struct eval<> ...    };Context classes are nothing more than a collection of specializations of anested `eval<>` class template. Each specialization handles a differentexpression type.In the [link boost_proto.users_guide.hello_calculator Hello Calculator]section, we saw an example of a user-defined context class for evaluatingcalculator expressions. That context class was implemented with the helpof Proto's _callable_context_. If we were to implement it from scratch, itwould look something like this:    // The calculator_context from the "Hello Calculator" section,    // implemented from scratch.    struct calculator_context    {        // 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 proto::tag_of<Expr>::type          , typename Arg0 = typename proto::child_c<Expr, 0>::type        >        struct eval;        // Handle placeholder terminals here...        template<typename Expr, int I>        struct eval<Expr, proto::tag::terminal, placeholder<I> >        {            typedef double result_type;            result_type operator()(Expr &, MyContext &ctx) const            {                return ctx.args[I];            }        };        // Handle other terminals here...        template<typename Expr, typename Arg0>        struct eval<Expr, proto::tag::terminal, Arg0>        {            typedef double result_type;            result_type operator()(Expr &expr, MyContext &) const            {                return proto::child(expr);            }        };        // Handle addition here...        template<typename Expr, typename Arg0>        struct eval<Expr, proto::tag::plus, Arg0>        {            typedef double result_type;            result_type operator()(Expr &expr, MyContext &ctx) const            {                return proto::eval(proto::left(expr), ctx)                     + proto::eval(proto::right(expr), ctx);            }        };        // ... other eval<> specializations for other node types ...    };Now we can use _eval_ with the context class above to evaluate calculatorexpressions as follows:    // Evaluate an expression with a calculator_context    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 givesyou complete control over how the expression is evaluated. The context class inthe [link boost_proto.users_guide.hello_calculator Hello Calculator] examplewas much simpler. In the next section we'll see the helper class Proto providesto ease the job of implementing context classes.[endsect][/================================================][section:canned_contexts Proto's Built-In Contexts][/================================================]Proto provides some ready-made context classes that you can use as-is, or thatyou can use to help while implementing your own contexts. They are:[variablelist  [ [[link boost_proto.users_guide.expression_evaluation.canned_contexts.default_context [^default_context]]]    [An evaluation context that assigns the usual C++ meanings to all the     operators. For example, addition nodes are handled by evaluating the     left and right children and then adding the results. The _default_context_     uses Boost.Typeof to deduce the types of the expressions it evaluates.] ]  [ [[link boost_proto.users_guide.expression_evaluation.canned_contexts.null_context [^null_context]]]    [A simple context that recursively evaluates children but does not combine     the results in any way and returns void.] ]  [ [[link boost_proto.users_guide.expression_evaluation.canned_contexts.callable_context [^callable_context<>]]]    [A helper that simplifies the job of writing context classes. Rather than

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?