users_manual.qbk

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

QBK
1,963
字号
Lazy functions are your friends. The library provides a facility to make lazyfunctions. The code below is a rewrite of the `is_odd` function using thefacility:    struct is_odd_impl    {        template <typename Arg>        struct result        {            typedef bool type;        };        template <typename Arg>        bool operator()(Arg arg1) const        {            return arg1 % 2 == 1;        }    };    function<is_odd_impl> is_odd;[h2 Things to note:]* `result` is a nested metafunction that reflects the return type of the  function (in this case, bool). This makes the function fully polymorphic:  It can work with arbitrary `Arg` types.* There are as many Args in the `result` metafunction as in the actual  `operator()`.* `is_odd_impl` implements the function.* `is_odd`, an instance of `function<is_odd_impl>`, is the lazy function.Now, `is_odd` is a truly lazy function that we can use in conjunction with therest of phoenix. Example:    find_if(c.begin(), c.end(), is_odd(arg1));(See [@../../example/users_manual/function.cpp function.cpp])[h2 Predefined Lazy Functions]The library is chock full of STL savvy, predefined lazy functions covering thewhole of the STL containers, iterators and algorithms. For example, there are lazyversions of container related operations such as assign, at, back, begin,pop_back, pop_front, push_back, push_front, etc. (See [link phoenix.containerContainer]).[endsect][section More]As mentioned earlier, this chapter is not a thorough discourse of the library.It is meant only to cover enough ground to get you into high gear as quickly aspossible. Some advanced stuff is not discussed here (e.g. [link phoenix.composite.scopeScopes]); nor are features that provide alternative (short-hand) ways to do thesame things (e.g. [link phoenix.composite.bind Bind] vs. Lazy Functions).[blurb __tip__ ...*If you still wish to learn more, the read on...*][endsect][endsect][section Basics][def __constant_n__ /n/][def __argument_n__ a/n/]Almost everything is a function in the Phoenix library that can be evaluated as`f(a1, a2, ..., __argument_n__)`, where __constant_n__ is the function's arity, or number of arguments that thefunction expects. Operators are also functions. For example, `a + b` is just afunction with arity == 2 (or binary). `a + b` is the same as `add(a, b)`, `a + b+ c` is the same as `add(add(a, b), c)`.[note Amusingly, functions may even return functions. We shall seewhat this means in a short while.][h2 Partial Function Application]Think of a function as a black box. You pass arguments and it returns somethingback. The figure below depicts the typical scenario.[$images/fbox.png]A fully evaluated function is one in which all the arguments are given. Allfunctions in plain C++ are fully evaluated. When you call the `sin(x)` function,you have to pass a number x. The function will return a result in return: thesin of x. When you call the `add(x, y)` function, you have to pass two numbers xand y. The function will return the sum of the two numbers. The figure below isa fully evaluated `add` function.[$images/adder.png]A partially applied function, on the other hand, is one in which not all thearguments are supplied. If we are able to partially apply the function `add`above, we may pass only the first argument. In doing so, the function does nothave all the required information it needs to perform its task to compute andreturn a result. What it returns instead is another function, a lambda function--another black box. Unlike the original `add` function which has an arity of 2,the resulting lambda function has an arity of 1. Why? because we alreadysupplied part of the input: `2`[$images/add2.png]Now, when we shove in a number into our lambda function, it will return 2 pluswhatever we pass in. The lambda function essentially remembers 1) the originalfunction, `add`, and 2) the partial input, 2. The figure below illustrates acase where we pass 3 to our lambda function, which then returns 5:[$images/add2_call.png]Obviously, partially applying the `add` function, as we see above, cannot bedone directly in C++ where we are expected to supply all the arguments that afunction expects. That's where the Phoenix library comes in. The libraryprovides the facilities to do partial function application.[h2 STL and higher order functions]So, what's all the fuss? What makes partial function application so useful?Recall our original example in the [link phoenix.starter_kit previous section]:    find_if(c.begin(), c.end(), arg1 % 2 == 1)The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a placeholderfor an argument to be supplied later. Hence, since there's only one unsupplied argument, thelambda function has an arity 1. It just so happens that `find_if` supplies theunsupplied argument as it loops from `c.begin()` to `c.end()`.[note Higher order functions are functions which can take otherfunctions as arguments, and may also return functions as results. Higher orderfunctions are functions that are treated like any other objects and can be used asarguments and return values from functions.][h2 Lazy Evaluation]In Phoenix, to put it more accurately, function evaluation has two stages:# Partial application# Final evaluationThe first stage is handled by a set of generator functions. These are your frontends (in the client's perspective). These generators create (through partialfunction application), higher order functions that can be passed on just likeany other function pointer or function object. The second stage, the actualfunction call, can be invoked or executed anytime in the future, or not at all;hence /"lazy"/.If we look more closely, the first step involves partial function application:    arg1 % 2 == 1The second step is the actual function invocation (done inside the `find_if`function. These are the back-ends (often, the final invocation is never actuallyseen by the client). In our example, the `find_if`, if we take a look inside,we'll see something like:    template <class InputIterator, class Predicate>    InputIterator    find_if(InputIterator first, InputIterator last, Predicate pred)    {        while (first != last && !pred(*first))  // <--- The lambda function is called here            ++first;                            //      passing in *first        return first;    }Again, typically, we, as clients, see only the first step. However, in thisdocument and in the examples and tests provided, don't be surprised to see thefirst and second steps juxtaposed in order to illustrate the complete semanticsof Phoenix expressions. Examples:    int x = 1;    int y = 2;    cout << (arg1 % 2 == 1)(x) << endl; // prints 1 or true    cout << (arg1 % 2 == 1)(y) << endl; // prints 0 or false[h2 Forwarding Function Problem]Usually, we, as clients, write the call-back functions while libraries (such asSTL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g.if you have to write an STL algorithm that takes in a predicate, or develop aGUI library that accepts event handlers, you have to be aware of a little knownproblem in C++ called the "__forwarding__".Look again at the code above:    (arg1 % 2 == 1)(x)Notice that, in the second-stage (the final evaluation), we used a variable `x`.Be aware that the second stage cannot accept non-const temporaries and literalconstants. Hence, this will fail:    (arg1 % 2 == 1)(123) // Error!Disallowing non-const rvalues partially solves the "__forwarding__" butprohibits code like above.[h2 Polymorphic Functions]Unless otherwise noted, Phoenix generated functions are fully polymorphic. Forinstance, the `add` example above can apply to integers, floating points, userdefined complex numbers or even strings. Example:    std::string h("Hello");    char const* w = " World";    std::string r = add(arg1, arg2)(h, w);evaluates to `std::string("Hello World")`. The observant reader might noticethat this function call in fact takes in heterogeneous arguments where `arg1` isof type `std::string` and `arg2` is of type `char const*`. `add` still worksbecause the C++ standard library allows the expression `a + b` where `a` is a`std::string` and `b` is a `char const*`.[endsect][section Organization]Care and attention to detail was given, painstakingly, to the design andimplementation of Phoenix.The library is organized in four layers:[$images/organization.png]The modules are orthogonal, with no cyclic dependencies.Lower layers do not depend on higher layers. Modules in a layer do not depend on other modules in the same layer.This means, for example, that Bind can be completely discarded if it isnot required; or one could perhaps take out Operator and Statement and just use Function,which may be desireable in a pure FP application.The library has grown from the original Phoenix but still comprises onlyheader files. There are no object files to link against.[h2 Core]The lowest two layers comprise the core.The `Actor` is the main concept behind the library. Lazy functions areabstracted as actors. There are only 2kinds of actors:# Primitives# CompositesPrimitives provide the basic building blocks of functionality within Phoenix.Composites are used to combine these primitives together to provide morepowerful functionality.Composites are composed of zero or more actors. Each actor in a composite canagain be another composite.[table Modules    [[Module]           [Description]]    [[Function]         [Lazy functions support (e.g. `add`)]]    [[Operator]         [Lazy operators support (e.g. `+`)]]    [[Statement]        [Lazy statments (e.g. `if_`, `while_`)]]    [[Object]           [Lazy casts (e.g. `static_cast_`),                        object creation destruction (e.g.                        `new_`, `delete_`)]]    [[Scope]            [Support for scopes, local variables and lambda-lambda]]    [[Bind]             [Lazy functions from free functions, member functions or member variables.]]    [[Container]        [Set of predefined "lazy" functions that work on STL                        containers and sequences (e.g. `push_back`).]]    [[Algorithm]        [Set of predefined "lazy" versions of the STL algorithms                        (e.g. `find_if`).]]]Each module is defined in a header file with the same name. For example,the core module is defined in `<boost/spirit/home/phoenix/core.hpp>`.[table Includes    [[Module]           [File]]    [[Core]             [`#include <boost/spirit/home/phoenix/core.hpp>`]]    [[Function]         [`#include <boost/spirit/home/phoenix/function.hpp>`]]    [[Operator]         [`#include <boost/spirit/home/phoenix/operator.hpp>`]]    [[Statement]        [`#include <boost/spirit/home/phoenix/statement.hpp>`]]    [[Object]           [`#include <boost/spirit/home/phoenix/object.hpp>`]]    [[Scope]            [`#include <boost/spirit/home/phoenix/scope.hpp>`]]    [[Bind]             [`#include <boost/spirit/home/phoenix/bind.hpp>`]]    [[Container]        [`#include <boost/spirit/home/phoenix/container.hpp>`]]    [[Algorithm]        [`#include <boost/spirit/home/phoenix/algorithm.hpp>`]]][blurb __tip__ Finer grained include files are available per feature; see thesucceeding sections.][endsect][section Actors]The `Actor` is the main concept behind the library. Actors are function objects.An actor can accept 0 to `PHOENIX_LIMIT` arguments.[note You can set `PHOENIX_LIMIT`, the predefined maximum arity anactor can take. By default, `PHOENIX_LIMIT` is set to 10.]Phoenix supplies an `actor` class template whose specializationsmodel the `Actor` concept.  `actor` has one template parameter, `Eval`,that supplies the smarts to evaluate the resulting function.    template <typename Eval>    struct actor : Eval    {        return_type        operator()() const;        template <typename T0>        return_type        operator()(T0& _0) const;        template <typename T0, typename T1>        return_type        operator()(T0& _0, T1& _1) const;        //...    };The actor class accepts the arguments through a set of function call operatorsfor 0 to `PHOENIX_LIMIT` arities (Don't worry about the details, for now. Note, for example,that we skimp over the details regarding `return_type`). The argumentsare then forwarded to the actor's `Eval` for evaluation.[endsect][section Primitives]Actors are composed to create more complex actors in a tree-like hierarchy. Theprimitives are atomic entities that are like the leaves in the tree. Phoenix isextensible. New primitives can be added anytime. Right out of the box, there areonly a few primitives. This section shall deal with these preset primitives.[section Arguments]    #include <boost/spirit/home/phoenix/core/argument.hpp>We use an instance of:    actor<argument<N> >to represent the Nth function argument. The argument placeholder acts as animaginary data-bin where a function argument will be placed.[h2 Predefined Arguments]There are a few predefined instances of `actor<argument<N> >` named`arg1`..`argN`, and its __bll__ counterpart `_1`..`_N`. (where N is a predefinedmaximum).Here are some sample preset definitions of `arg1`..`argN`    actor<argument<0> > const arg1 = argument<0>();    actor<argument<1> > const arg2 = argument<1>();    actor<argument<2> > const arg3 = argument<2>();and its __bll__ `_1`..`_N` style counterparts:    actor<argument<0> > const _1 = argument<0>();    actor<argument<1> > const _2 = argument<1>();    actor<argument<2> > const _3 = argument<2>();[note You can set `PHOENIX_ARG_LIMIT`, the predefined maximumplaceholder index. By default, `PHOENIX_ARG_LIMIT` is set to `PHOENIX_LIMIT`(See [link phoenix.actors Actors]).][h2 User Defined Arguments]When appropriate, you can define your own `argument<N>` names. For example:    actor<argument<0> > x; // note zero based index`x` may now be used as a parameter to a lazy function:    add(x, 6)which is equivalent to:    add(arg1, 6)[h2 Evaluating an Argument]An argument, when evaluated, selects the Nth argument from the those passedin by the client.For example:    char        c = 'A';    int         i = 123;    const char* s = "Hello World";    cout << arg1(c) << endl;        //  Get the 1st argument: c    cout << arg1(i, s) << endl;     //  Get the 1st argument: i    cout << arg2(i, s) << endl;     //  Get the 2nd argument: swill print out:

⌨️ 快捷键说明

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