construction.qbk
来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 1,026 行 · 第 1/3 页
QBK
1,026 行
The application of unary `operator+` on the last line is equivalent tothe by-ref invocation of _make_expr_ because Proto's operator overloadsalways build trees by holding nodes by reference.If you specify a domain when invoking _make_expr_, then _make_expr_will use that domain's generator to wrap the resulting node in adomain-specific wrapper. In the example below, expressions within the`MyDomain` domain are wrapped in a `MyExpr<>` wrapper. template<typename Expr> struct MyExpr; struct MyDomain : proto::domain<proto::generator<MyExpr> > {}; // ... // Use result_of::make_expr<> to compute the return type: int i = 0; typedef proto::result_of::make_expr< MyTag , MyDomain // <-- Note second template , int // param can be a domain. , char >::type expr_type; // Construct an expression within MyDomain: expr_type expr = proto::make_expr<MyTag, MyDomain>(i, 'a'); // expr_type is the same as this type: typedef // New node is wrapped in MyExpr<> MyExpr<proto::binary_expr< MyTag // Terminals are also wrapped. , MyExpr<proto::terminal<int>::type> , MyExpr<proto::terminal<char>::type> >::type> expr_type2; BOOST_MPL_ASSERT((is_same<expr_type2, expr_type>));[/======================================================][heading Building Expression Trees With [^unpack_expr()]][/======================================================][:[*Synopsys:]] namespace proto { namespace result_of { // Metafunction for calculating the return type // of the unpack_expr() function template< typename Tag , typename DomainOrSequence , typename SequenceOrVoid = void > struct unpack_expr { typedef __implelemtation_defined__ type; }; } namespace functional { // A callable function object equivalent of the // unpack_expr() function. template<typename Tag, typename Domain = default_domain> struct unpack_expr : callable { template<typename Sig> struct result; template<typename This, typename Sequence> struct result<This(Sequence)> : result_of::unpack_expr<Tag, Domain, Sequence> {}; template<typename Sequence> typename result_of::unpack_expr<Tag, Domain, Sequence>::type operator ()(Sequence const &sequence) const; }; } // The unpack_expr() function template<typename Tag, typename Domain, typename Sequence> typename result_of::unpack_expr<Tag, Domain, Sequence>::type unpack_expr(Sequence const &sequence); }Once you understand _make_expr_, understanding _unpack_expr_ issimple. It behaves exactly the same way, except that rather thanpassing children individually, you pass the children as a Fusionsequence. So for instance, the following are equivalent: // Build an expression with make_expr(): int i = 0; proto::make_expr<Tag>(i, 'a'); // Build the same expression with unpack_expr(): proto::unpack_expr<Tag>(fusion::make_tuple(i, 'a')); // Also the same as the above: fusion::tuple<int, char> args(i, 'a'); proto::unpack_expr<Tag>(args);If you would like the arguments to be stored by reference, you canuse `boost::ref()`, just as with _make_expr_. // Hold one argument by reference: int i = 0; proto::unpack_expr<Tag>(fusion::make_tuple(boost::ref(i), 'a')); // Also the same as the above: fusion::tuple<int &, char> args(i, 'a'); proto::unpack_expr<Tag>(args);As with _make_expr_, _unpack_expr_ has a corresponding metafunctionin the `proto::result_of` namespace for calculating its return type, aswell as a callable function object form in the `proto::functional`namespace.One last interesting point about _unpack_expr_: Proto expressionnodes are themselves valid Fusion sequences. Here, for instance, isa clever way to use _unpack_expr_ to turn a binary plus node intoa binary minus node: // Use unpack_expr() to turn an addition into a subtraction proto::literal<int> i(8), j(42); proto::unpack_expr<proto::tag::minus>( i + j );The expression `i + j` creates an expression tree which _unpack_expr_interprets as a sequence of its children `i` and `j`. The result is anew node with the `tag::minus` tag and `i` and `j` as children.[/=====================================================][heading Generating Custom Expression Factory Functions][/=====================================================][:[*Synopsys:]] // Generate BOOST_PROTO_MAX_ARITY overloads of a // function template named NAME within a particular // DOMAIN that generates expressions with a given // TAG and optionally has some arguments bound. #define BOOST_PROTO_DEFINE_VARARG_FUNCTION_TEMPLATE( \ NAME \ , DOMAIN \ , TAG \ , BOUNDARGS \ )The `proto::functional::make_expr<>` function object makes it very simpleto create something that behaves like an expression factory function. Forinstance, the following defines a factory named `invert()` that "complements" its argument; that is, it builds a new node with type `tag::complement` as if Proto's `operator~` had been applied: // invert(x) is now a synonym for ~proto::as_expr(x) proto::functional::make_expr<proto::tag::complement> const invert = {};Such named "operators" are very important for domain-specific embeddedlanguages. What's more, when defined as above, the `invert()` factory canaccept up to `BOOST_PROTO_MAX_ARITY` arguments, although in this casethat wouldn't be particularly meaningful.But imagine if you have a custom tag type `foo_tag<>` that is a template.You would like to define a `foo()` factory function that itself was a template,like this: template<typename T, typename A0> typename proto::result_of::make_expr< foo_tag<T> , A0 const & >::type foo(A0 const &a0) { return proto::make_expr<foo_tag<T> >(boost::ref(a0)); }Now, users of your function can invoke it like this: `foo<int>("foo!")`. Ifyou want to seamlessly handle up to /N/ argument, you have to write all /N/overloads yourself --- `functional::make_expr<>` can't help you. For thissituation, Proto provides the `BOOST_PROTO_DEFINE_VARARG_FUNCTION_TEMPLATE()`macro. You can invoke it as follows: // Generate overloads of the foo() function template // like the one above BOOST_PROTO_DEFINE_VARARG_FUNCTION_TEMPLATE( \ foo \ , proto::default_domain \ , (foo_tag)(typename) \ , BOOST_PP_SEQ_NIL \ )The first macro parameter specified the name of the function template, `foo`.The second parameter is the domain of the resulting expression. The thirdparameter is the tag type, specified as a Boost.Preprocessor sequence. Atag template like `foo_tag<typename>` is represented as a PP sequence like`(foo_tag)(typename)`. Had `foo_tag<>` been defined instead as `template<typename, int> struct foo_tag`, that would be a PP sequence like`(foo_tag)(typename)(int)`.The last macro parammeter, `BOOST_PP_SEQ_NIL`, is used for specifying anyadditional implicit arguments. There are none in this case, so `BOOST_PP_SEQ_NIL` is used to represent an empty sequence.As another example, consider a DSEL like the Boost Lambda Library, for which you might want a function named `construct()` for doing deferred construction of objects. You might want users to be able to use it likethis: std::vector<S> buffer; // Use a lambda to construct S objects using two // sequences as constructor arguments: std::transform( sequence1.begin() , sequence1.end() , sequence2.begin() , std::back_inserter(buffer) , construct<S>(_1, _2) // From a hypothetical lambda DSEL );How might the `construct()` function be defined? We would like it to returna lazy function invocation that, when evaluated with two arguments, causes`S` objects to be constructed. Lazy functions in Proto look like this: // The type of a Proto lazy function proto::function< TheFunctionToCall , Argument1 , Argument2 , ... >::typeIn the above, `TheFunctionToCall` might be an ordinary function object, solet's define a `construct_helper<>` function object that constructs an object. template<typename T> struct construct_helper { typedef T result_type; // for TR1 result_of T operator()() const { return T(); } template<typename A0> T operator()(A0 const &a0) const { return T(a0); } // ... other overloads ... };With such a function object, we can say `construct_helper<S>()(1, 'a')` toimmediately construct an `S` object using `1` and `'a'` as constructorarguments. We want this to be lazy, so we can wrap `construct_helper<S>` ina Proto terminal. // A lazy S constructor terminal<construct_helper<S> >::type const construct_S = {{}}; // OK, make a lazy function invocation but don't call it. construct_S(1, 'a'); // Calls the lazy function and constructs an S proto::default_context ctx; S s = proto::eval( construct_S(1, 'a'), ctx );We're closer, but this is not the syntax we want. Recall that we wantusers to create objects lazily with `construct<S>(_1, _2)`. We canget that syntax with the following: // Define the construct() function template that // constructs an object lazily. template<typename T, typename A0, typename A1> typename proto::result_of::make_expr< proto::tag::function , construct_helper<T> const , A0 const & , A1 const & >::type const construct(A0 const &a0, A1 const &a1) { return proto::make_expr<proto::tag::function>( construct_helper<T>() , boost::ref(a0) , boost::ref(a1) ); }Now users can say `construct<S>(_1, _2)` and get the lazy objectconstruction they want. (Making it work with `std::transform()`takes a little more effort, but that's covered in the [link boost_proto.users_guide.examples.lambda Lambda] example.)Now we need /N/ overloads to handle up to /N/ arguments. That's a lotof boiler plate, so we can use the `BOOST_PROTO_DEFINE_VARARG_FUNCTION_TEMPLATE()`macro to simplify our job. // Generate BOOST_PROTO_MAX_ARITY-1 overloads of the // construct function template like the one defined above. BOOST_PROTO_DEFINE_VARARG_FUNCTION_TEMPLATE( \ construct \ , MyLambdaDomain \ , (proto::tag::function) \ , ((construct_helper)(typename)) \ )What is new in this case is the fourth macro argument, which specifiesthat there is an implicit first argument to `construct()` of type `construct_helper<X>`, where `X` is a template parameter of the function. The fourth argument to the macro is actually a PP sequence of PP sequences. Each sequence describes one implicit argument.To see `BOOST_PROTO_DEFINE_VARARG_FUNCTION_TEMPLATE()` and `construct()` inaction, please check out the[link boost_proto.users_guide.examples.lambda Lambda] example.[blurb [*Ugly Macros]You may find this use of the preprocessor distasteful and decide towrite out the overloads yourself. That's fine, but there are some goodreasons to consider the macro.1) You may not be able to anticipate the maximum number of arguments your users will require. If users decide to increase `BOOST_PROTO_MAX_ARITY`, the macro will automatically generate the additional overloads for you.2) On compilers that support variadic templates, you'd rather this generated just one variadic function instead of /N/ overloads, but you'd like your code to be portable to compilers that don't support variadic templates. This is possible if you use the macro, but not otherwise. (Proto doesn't yet support variadic templates but it will in the future.)][endsect][endsect]
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?