transforms.qbk
来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 1,417 行 · 第 1/4 页
QBK
1,417 行
The `fold_tree<>` and `reverse_fold_tree<>` transforms are unlike the othertransforms that Proto provides in that they operate on entire sub-trees ratherthan just single nodes within the tree.These are higher-level transforms, implemented in terms of the `fold<>`and `reverse_fold<>` transforms and helper structs `fold_tree_<>` and`reverse_fold_tree_<>`, one of which is shown below: // fold_tree_ either recurses into the expression, if its Grammar // matches, or else ends the recursion by matching Grammar and // applying its transform. template<typename Grammar, typename Fun> struct fold_tree_ : or_< when<Grammar, fold<_, _state, fold_tree_<Grammar, Fun> > > , when<_, Fun> > {}; The `reverse_fold_tree_<>` helper is specified similarly, only with`reverse_fold<>` instead of `fold<>`. With these two helpers, we canspecify the behavior of `fold_tree<>` and `reverse_fold_tree<>` asfollows:[table [ [Expression] [Returns] ] [ [``fold_tree<Sequence, State0, Fun> ::result<void(Expr, State, Visitor)>::type``] [``fold< Sequence , State0 , fold_tree_< nary_expr<Expr::proto_tag, vararg<_> > , Fun >>::result<void(Expr, State, Visitor)>::type``] ] [ [`fold_tree<Sequence, State0, Fun>()(expr, state, visitor)`] [``fold< Sequence , State0 , fold_tree_< nary_expr<Expr::proto_tag, vararg<_> > , Fun >>()(expr, state, visitor)``] ] [ [``reverse_fold_tree<Sequence, State0, Fun> ::result<void(Expr, State, Visitor)>::type``] [``reverse_fold< Sequence , State0 , reverse_fold_tree_< nary_expr<Expr::proto_tag, vararg<_> > , Fun >>::result<void(Expr, State, Visitor)>::type``] ] [ [`reverse_fold_tree<Sequence, State0, Fun>()(expr, state, visitor)`] [``reverse_fold< Sequence , State0 , reverse_fold_tree_< nary_expr<Expr::proto_tag, vararg<_> > , Fun >>()(expr, state, visitor)``] ]]Example:[FoldTreeToList][endsect][section:pass_through [^pass_through<>]] namespace boost { namespace proto { namespace transform { template<typename Grammar> struct pass_through; } using transform::pass_through; }}The `pass_through<>` transform iterates over the pairs ofchildren in the grammar and the expression, applying the child grammar'stransform to the corresponding child expression. The resulting transformedchildren expressions are reassembled back into an expression of the sametype as the parent expression.As a side-effect, `pass_through<>` transforms all sub-expressions held byreference into ones held by value.Note that all expression generator meta-functions (Eg., `posit<>`, `shift_right<>`, `function<>`, `nary_expr<>`, etc.) have a pass-throughtransform by default, so there is rarely any need to use the `pass_through<>`transform explicitly.[table [ [Expression] [Returns] ] [ [``transform::pass_through<Grammar> ::result<void(Expr, State, Visitor)>::type``] [``typename nary_expr< Expr::proto_tag , result_of::arg_c<Grammar, 0>::type::result< void(result_of::arg_c<Expr, 0>::type, State, Visitor)>::type , result_of::arg_c<Grammar, 1>::type::result< void(result_of::arg_c<Expr, 1>::type, State, Visitor)>::type // ... , result_of::arg_c<Grammar, N>::type::result< void(result_of::arg_c<Expr, N>::type, State, Visitor)>::type>::type``] ] [ [`transform::pass_through<Grammar>()(expr, state, visitor)`] [``transform::pass_through<Grammar> ::result<void(Expr, State, Visitor)>::type::make( result_of::arg_c<Grammar, 0>::type()( proto::arg_c<0>(expr), state, visitor) , result_of::arg_c<Grammar, 1>::type()( proto::arg_c<1>(expr), state, visitor) // ... , result_of::arg_c<Grammar, N>::type()( proto::arg_c<N>(expr), state, visitor) )``] ]]Example:[Promote][endsect][endsect][/======================================================][section:user_defined_transforms User-Defined Transforms][/======================================================]In previous sections, we've seen how to compose larger transformsout of smaller transforms using function types.We've also seen the primitive transforms that Proto provides. So-called primitive transforms can be used without specifyingarguments, like `_arg0` which returns the first child of the current node. But primitive transforms are not special in any way.They are merely ternary function objects that take the currentexpression, state and visitor as arguments.You can define your own primitive transforms. You might want todo this if your transform is complicated and composing it outof primitives becomes unwieldy. You might also do thisto work around compiler bugs on legacy compilers that makescomposing transforms using function types problematic. Finally,you might also decide to define your own primitive transformsto improve compile times. Since Proto can simply invoke aprimitive transform directly without having to process argumentsor differentiate callable transforms from object transforms,primitive transforms are more efficient.To define your own primitive transform, merely define a ternaryfunction object that accepts an expression, a state and a visitor.Here, for example, is how Proto defines the `arg_c<>` transform: namespace boost { namespace proto { namespace transform { // A transform that returns the I-th child template<int I> struct arg_c : callable { // Nested "result" template, used by // boost::result_of<> to compute the // return type of the function. template<typename Sig> struct result; template<typename This, typename Expr, typename State, typename Visitor> struct result<This(Expr, State, Visitor)> : proto::result_of::arg_c<Expr, I> {}; // Function call operator that actually // executes the transform. template<typename Expr, typename State, typename Visitor> typename proto::result_of::arg_c<Expr, I>::const_reference operator ()(Expr const &expr, State const &, Visitor &) const { return proto::arg_c<I>(expr); } }; }}} namespace boost { namespace proto { // Note that arg_c<I> is callable, so that // it can be used with arguments, as: // arg_c<0>(arg_c<1>) template<int I> struct is_callable<transform::arg_c<I> > : mpl::true_ {}; }}There is nothing particularly special about the definition of`arg_c<>`. It is just an ordinary polymorphic function object. Theonly interesting bit is the `is_callable<>` specialization, whichwill be described in the section called "Making Your Transform Callable".Once you have defined such a ternary function object, you can useit as a transform without any arguments and Proto will automaticallypass it the current expression, state and visitor parameters.[endsect][/=================================================][section:is_callable Making Your Transform Callable][/=================================================]Transforms are typically of the form `when< Something, R(A0,A1,...) >`. Thequestion is whether `R` represents a function to call or an object toconstruct, and the answer determines how `when<>` evaluates the transform.`when<>` uses the `is_callable<>` trait to disambiguate between the two.Proto does its best to guess whether a transform is callable or not, butit doesn't always get it right. It's best to know the rules Proto uses,so that you know when you need to specialize `is_callable<>`.The first thing to know is that templates are not considered callableby default. This is true ['even if the template inherits from `proto::callable`]. Consider the following erroneous transform: // Proto can't tell this defines a // callable transform! template<typename T> struct times2 : callable { typedef T result_type; T operator()(T i) const { return i * 2; } }; // ERROR! This is not going to // multiply the int by 2. struct IntTimes2 : when< terminal<int>, times2<int>(_arg) > {};The problem is that Proto doesn't know that `times2<int>` is a callabletransform. Instead, it assumes it's an object transform and will try to construct a `times2<int>` object and initialize it will an `int`. Thatwill not compile.[note Why can't Proto tell that `times2<int>` is callable? After all,it inherits from `proto::callable`, and that is detectable, right?In fact, determining whether some type `X<Y>` inherits from`callable` will cause the template `X<Y>` to be instantiated. That's aproblem for a type like `std::vector<_arg(_arg1)>()`, which is a validtransform that default-constructs a particular instantiation of `std::vector<>`. But `std::vector<>` will not suffer to be instantiatedwith `_arg(_arg1)` as a template parameter! As a result, Proto hasto assume that a type `X<Y>` represents an object transform and nota callable transform.]There are a couple of solutions to the `times2<int>` problem. One solution is to wrap the transform in `call<>`. This forces Proto totreat `times2<int>` as callable: // OK, calls times2<int> struct IntTimes2 : when< terminal<int>, call<times2<int>(_arg)> > {};This can be a bit of a pain, because we need to wrap every use of`times2<int>`, which can be tedious and error prone, and makes ourgrammar cluttered and harder to read.Another solution is to specialize `proto::is_callable<>` on our `times2<>` template: namespace boost { namespace proto { template<typename T> struct is_callable<times2<T> > : mpl::true_ {}; }} // OK, times2<> is callable struct IntTimes2 : when< terminal<int>, times2<int>(_arg) > {};This is better, but still a pain because of the need to openProto's namespace.You could simply make sure that the transform is nota template. Consider the following: // No longer a template! struct times2int : times2<int> {}; // OK, times2int is callable struct IntTimes2 : when< terminal<int>, times2int(_arg) > {};This works because now Proto can tell that `times2int` inherits(indirectly) from `proto::callable`. Any non-template types canbe safely checked for inheritance because, as they are nottemplates, there is no worry about instantiation errors.There is one last way to tell Proto that `times2<>` is callable.You could add an extra dummy template parameter that defaultsto `proto::callable`: // Proto will recognize this as callable template<typename T, typename Dummy = callable> struct times2 : callable { typedef T result_type; T operator()(T i) const { return i * 2; } }; // OK, this works! struct IntTimes2 : when< terminal<int>, times2<int>(_arg) > {};Note that in addition to the extra template parameter, `times2<>`still inherits from `callable`. That's not necessary in this examplebut it's good style because any types derived from `times2<>` (as`times2int` defined above) will still be considered callable.[endsect][endsect]
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?