calculator.qbk

来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 177 行

QBK
177
字号
[/ / Copyright (c) 2008 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 Hello Calculator]"Hello, world" is nice, but it doesn't get you very far. Let's use Proto tobuild a Domain Specific Embedded Language for a lazily-evaluated calculator.We'll see how to define the terminals in your mini-language, how Proto combinesthe terminals into larger expressions, and how to define an evaluation contextso that your expressions can do useful work. When we're done, we'll have amini-language that will allow us to declare a lazily-evaluated arithmeticexpression, such as `(_2 - _1) / _2 * 100`, where `_1` and `_2` areplaceholders for values to be passed in when the expression is evaluated.[heading Defining Terminals]The first order of business is to define the placeholders `_1` and `_2`. Forthat, we'll use the _terminal_ metafunction.    // Define some placeholder types    struct placeholder1 {};    struct placeholder2 {};    // Define the Proto-ified placeholder terminals    proto::terminal< placeholder1 >::type const _1 = {{}};    proto::terminal< placeholder2 >::type const _2 = {{}};The initialization may look a little odd at first, but there is a good reasonfor doing things this way. The objects `_1` and `_2` above do not requirerun-time construction -- they are ['statically initialized], which means theyare essentially initialized at compile time. See the[link boost_proto.appendices.rationale.static_initialization StaticInitialization] section in the [link boost_proto.appendices.rationale Rationale]appendix for more information.[heading Constructing Expression Trees]Now that we have terminals, we can use Proto's operator overloads to combinethese terminals into larger expressions. So, for instance, we can immediatelycreate the expression `(_2 - _1) / _2 * 100`. This creates an expression treewith a node for each operator. The type of the resulting object is large andcomplex, but we are not terribly interested in it.So far, the object is just a tree representing the expression. It has nobehavior. In particular, it is not yet a calculator. Below we'll see howto make it a calculator by defining an evaluation context.[heading Defining an Evaluation Context]No doubt you want your expression templates to actually /do/ something. Oneapproach is to define an ['evaluation context]. The context is like a functionobject that associates behaviors with the node types in your expression tree.An example should make it clear.    struct calculator_context      : proto::callable_context< calculator_context const >    {        calculator_context(double d1, double d2)          : d1_(d1), d2_(d2)        {}        // Define the result type of the calculator.        // (This makes the the calculator_context "callable".)        typedef double result_type;        // Handle the placeholders:        double operator()(proto::tag::terminal, placeholder1) const        {            return this->d1_;        }        double operator()(proto::tag::terminal, placeholder2) const        {            return this->d2_;        }        // Handle literals:        double operator()(proto::tag::terminal, double d) const        {            return d;        }        // Handle addition:        template< typename Left, typename Right >        double operator()(proto::tag::plus, Left const &left, Right const &right) const        {            return proto::eval(left, *this) + proto::eval(right, *this);        }        // Handle subtraction:        template< typename Left, typename Right >        double operator()(proto::tag::minus, Left const &left, Right const &right) const        {            return proto::eval(left, *this) - proto::eval(right, *this);        }        // Handle multiplication:        template< typename Left, typename Right >        double operator()(proto::tag::multiplies, Left const &left, Right const &right) const        {            return proto::eval(left, *this) * proto::eval(right, *this);        }        // Handle division:        template< typename Left, typename Right >        double operator()(proto::tag::divides, Left const &left, Right const &right) const        {            return proto::eval(left, *this) / proto::eval(right, *this);        }    private:        double d1_, d2_;    };Notice how the binary arithmetic operations are handled. First the left andright operands are evaluated by invoking `proto::eval()`. After the left andright children are evaluated, the results are combined using the appropriatearithmetic operation.Now that we have an evaluation context for our calculator, we can use it toevaluate our arithmetic expressions, as below:    // Define a calculator context, where _1 is 45 and _2 is 50    calculator_context ctx( 45, 50 );    // Create an arithmetic expression and immediately evaluate it    double d = proto::eval( (_2 - _1) / _2 * 100, ctx );    // This prints "10"    std::cout << d << std::endl;[heading Default Expression Evaluation]You might notice that the `calculator_context` has a lot of boilerplate. Itis fairly common for addition nodes to be handled by evaluating the left andright children and then adding the result, for instance. For this purpose,Boost.Proto provides the _default_context_, which gives the operators their usualmeanings, and uses Boost.Typeof to deduce return types. In fact, the_callable_context_ from which our `calculator_context` inherits uses_default_context_ as a fall-back for any expression types you don't handleexplicitly. We can use that fact to simplify our `calculator_context`considerably:    struct calculator_context      : proto::callable_context< calculator_context const >    {        calculator_context(double d1, double d2)          : d1_(d1), d2_(d2)        {}        // Define the result type of the calculator.        // (This makes the the calculator_context "callable".)        typedef double result_type;        // Handle the placeholders:        double operator()(proto::tag::terminal, placeholder1) const        {            return this->d1_;        }        double operator()(proto::tag::terminal, placeholder2) const        {            return this->d2_;        }    private:        double d1_, d2_;    };That's pretty simple![endsect]

⌨️ 快捷键说明

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