front_end.qbk

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

QBK
554
字号
[/=======================================================]To use _extends_, your extension type must derive from _extends_. Unfortunately, that means that your extension type is no longer POD and its instances cannot be /statically initialized/. (See the [link boost_proto.appendices.rationale.static_initialization StaticInitialization] section in the [link boost_proto.appendices.rationale Rationale] appendix for why this matters.) In particular, as defined above, the global placeholder objects `_1` and `_2` will need to be initialized at runtime, which could lead to subtle order of initialization bugs.There is another way to make an expression extension that doesn't sacrifice POD-ness : the `BOOST_PROTO_EXTENDS()` macro. You can use it much like you use _extends_. We can use `BOOST_PROTO_EXTENDS()` to keep `calculator<>` a POD and our placeholders statically initialized.    // The calculator<> expression wrapper makes expressions    // function objects.    template< typename Expr >    struct calculator    {        // Use BOOST_PROTO_EXTENDS() instead of proto::extends<> to        // make this type a Proto expression extension.        BOOST_PROTO_EXTENDS(Expr, calculator<Expr>, calculator_domain)        typedef double result_type;        result_type operator()( double d1 = 0.0, double d2 = 0.0 ) const        {            /* ... as before ... */        }    };With the new `calculator<>` type, we can redefine our placeholders to be statically initialized:    calculator< proto::terminal< placeholder<0> >::type > const _1 = {{{}}};    calculator< proto::terminal< placeholder<1> >::type > const _2 = {{{}}};We need to make one additional small change to accommodate the POD-ness of our expression extension, which we'll describe below in the section on expression generators.What does `BOOST_PROTO_EXTENDS()` do? It defines a data member of expression type being extended; some nested typedefs that Proto requires; `operator=`, `operator[]` and `operator()` overloads for building expression templates; and a nested `result<>` template for calculating the return type of `operator()`. In this case, however, the `operator()` overloads and the `result<>` template are not needed because we are defining our own `operator()` in the `calculator<>` type. Proto provides additional macros for finer control over which member functions are defined. We could improve our `calculator<>` type as follows:    // The calculator<> expression wrapper makes expressions    // function objects.    template< typename Expr >    struct calculator    {        // Use BOOST_PROTO_BASIC_EXTENDS() instead of proto::extends<> to        // make this type a Proto expression extension:        BOOST_PROTO_BASIC_EXTENDS(Expr, calculator<Expr>, calculator_domain)        // Define operator[] to build expression templates:        BOOST_PROTO_EXTENDS_SUBSCRIPT()        // Define operator= to build expression templates:        BOOST_PROTO_EXTENDS_ASSIGN()        typedef double result_type;        result_type operator()( double d1 = 0.0, double d2 = 0.0 ) const        {            /* ... as before ... */        }    };Notice that we are now using `BOOST_PROTO_BASIC_EXTENDS()` instead of `BOOST_PROTO_EXTENDS()`. This just adds the data member and the nested typedefs but not any of the overloaded operators. Those are added separately with `BOOST_PROTO_EXTENDS_ASSIGN()` and `BOOST_PROTO_EXTENDS_SUBSCRIPT()`. We are leaving out the function call operator and the nested `result<>` template that could have been defined with Proto's `BOOST_PROTO_EXTENDS_FUNCTION()` macro.In summary, here are the macros you can use to define expression extensions, and a brief description of each.[def __expression__ [~expression]][def __extension__ [~extension]][def __domain__ [~domain]][table Expression Extension Macros [[Macro]  [Purpose]] [[``BOOST_PROTO_BASIC_EXTENDS(    __expression__  , __extension__  , __domain__)``]  [Defines a data member of type `__expression__` and some nested typedefs that Proto requires.]] [[`BOOST_PROTO_EXTENDS_ASSIGN()`]  [Defines `operator=`. Only valid when preceded by `BOOST_PROTO_BASIC_EXTENDS()`.]] [[`BOOST_PROTO_EXTENDS_SUBSCRIPT()`]  [Defines `operator[]`. Only valid when preceded by `BOOST_PROTO_BASIC_EXTENDS()`.]] [[`BOOST_PROTO_EXTENDS_FUNCTION()`]  [Defines `operator()` and a nested `result<>` template for return type calculation. Only valid when preceded by `BOOST_PROTO_BASIC_EXTENDS()`.]] [[``BOOST_PROTO_EXTENDS(    __expression__  , __extension__  , __domain__)``]  [Equivalent to:``    BOOST_PROTO_BASIC_EXTENDS(__expression__, __extension__, __domain__)    BOOST_PROTO_EXTENDS_ASSIGN()    BOOST_PROTO_EXTENDS_SUBSCRIPT()    BOOST_PROTO_EXTENDS_FUNCTION()``]]][endsect][/============================][section Expression Generators][/============================]The last thing that remains to be done is to tell Proto that it needs to wrap all of our calculator expressions in our `calculator<>` wrapper. We have already wrapped the placeholders, but we want /all/ expressions that involve the calculator placeholders to be calculators. We can do that by specifying an expression generator when we define our `calculator_domain`, as follows:    // Define the calculator_domain we forward-declared above.    // Specify that all expression in this domain should be wrapped    // in the calculator<> expression wrapper.    struct calculator_domain      : proto::domain< proto::generator< calculator > >    {};The first template parameter to `proto::domain<>` is the generator. "Generator" is just a fancy name for a function object that accepts an expression and does something to it. `proto::generator<>` is a very simple one --- it wraps an expression in the wrapper you specify. `proto::domain<>` inherits from its generator parameter, so all domains are themselves function objects.If we used `BOOST_PROTO_EXTENDS()` to keep our expression extension type POD, then we need to use `proto::pod_generator<>` instead of `proto::generator<>`, as follows:    // If calculator<> uses BOOST_PROTO_EXTENDS() instead of     // use proto::extends<>, use proto::pod_generator<> instead    // of proto::generator<>.    struct calculator_domain      : proto::domain< proto::pod_generator< calculator > >    {};[def __Domain__ [~Domain]]After Proto has calculated a new expression type, it checks the domains of the  child expressions. They must match. Assuming they do, Proto creates the new expression and passes it to `__Domain__::operator()` for any additional processing.  If we don't specify a generator, the new expression gets passed through unchanged.  But since we've specified a generator above, `calculator_domain::operator()`  returns `calculator<>` objects.Now we can use calculator expressions as function objects to STL algorithms, as follows:    double data[] = {1., 2., 3., 4.};    // Use the calculator DSEL to square each element ... WORKS! :-)    std::transform( data, data + 4, data, _1 * _1 );[endsect][/==========================================================][section:inhibiting_overloads Controlling Operator Overloads][/==========================================================]By default, Proto defines every possible operator overload for Protofiedexpressions. This makes it simple to bang together a DSEL. In some cases, however, the presence of Proto's promiscuous overloads can lead to confusion or worse. When that happens, you'll have to disable some of Proto's overloaded operators. That is done by defining the grammar for your domain and specifying it as the second parameter of the _domain_ template.In the [link boost_proto.users_guide.getting_started.hello_calculator Hello Calculator] section, we saw an example of a Proto grammar, which is repeated here:    // Define the grammar of calculator expressions    struct calculator_grammar      : proto::or_<            proto::plus< calculator_grammar, calculator_grammar >          , proto::minus< calculator_grammar, calculator_grammar >          , proto::multiplies< calculator_grammar, calculator_grammar >          , proto::divides< calculator_grammar, calculator_grammar >          , proto::terminal< proto::_ >        >    {};We'll have much more to say about grammars in subsequent sections, but for now, we'll just say that the `calculator_grammar` struct describes a subset of all expression types -- the subset that comprise valid calculator expressions. We would like to prohibit Proto from creating a calculator expression that does not conform to this grammar. We do that by changing the definition of the `calculator_domain` struct.[def __calculator_grammar__ [*calculator_grammar]]    // Define the calculator_domain. Expressions in the calculator    // domain are wrapped in the calculator<> wrapper, and they must    // conform to the calculator_grammar:    struct calculator_domain      : proto::domain< proto::generator< calculator >, __calculator_grammar__  >    {};The only new addition is `calculator_grammar` as the second template parameter to the _domain_ template. That has the effect of disabling any of Proto's operator overloads that would create an invalid calculator expression.Another common use for this feature would be to disable Proto's unary `operator&` overload. It may be surprising for users of your DSEL that they cannot take the address of their expressions! You can very easily disable Proto's unary `operator&` overload for your domain with a very simple grammar, as below:    // For expressions in my_domain, disable Proto's    // unary address-of operator.    struct my_domain      : proto::domain<            proto::generator< my_wrapper >            // A simple grammar that matches any expression that            // is not a unary address-of expression.          , proto::not_< proto::address_of< _ > >        >    {};The type `proto::not_< proto::address_of< _ > >` is a very simple grammar that matches all expressions except unary address-of expressions. In the section describing Proto's intermediate form, we'll have much more to say about grammars.[endsect][endsect][endsect]

⌨️ 快捷键说明

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