evaluation.qbk

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

QBK
560
字号
[/ / 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:expression_evaluation Expression Evaluation: Imparting Behaviors Within 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 to actually /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;            };        }                functional::eval const eval = {};    }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 sections 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 Expr::proto_tag        >        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_contest from the "Hello Calculator" section,    // 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)        {}        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 arg_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>        {            typedef double result_type;                        result_type operator()(Expr &, MyContext &ctx) const            {                // replaces _1 with the value in ctx.d2_                return ctx.d2_;            }        };        // 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::arg(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 ...        double d1_, d2_;    };Now we can use _eval_ with the context class above to evaluate calculatorexpressions as follows:    // Evaluate an expression with a calculator_context    double d = proto::eval(_1 + _2, calculator_context(5, 6));    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 Canned 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     writing template specializations, with _callable_context_ you write a     function object with an overloaded function call operator. Any expressions     not handled by an overload are automatically dispatched to a default     evaluation context that you can specify.] ]][/=========================================][section:default_context [^default_context]][/=========================================]The _default_context_ is an evaluation context that assigns the usual C++meanings to all the operators. For example, addition nodes are handled byevaluating the left and right children and then adding the results. The_default_context_ uses Boost.Typeof to deduce the types of the expressions itevaluates.For example, consider the following "Hello World" example:    #include <iostream>

⌨️ 快捷键说明

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