intermediate_form.qbk
来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 1,171 行 · 第 1/3 页
QBK
1,171 行
[[unary `!`] [`proto::tag::logical_not`] [`proto::logical_not<>`]] [[unary prefix `++`] [`proto::tag::pre_inc`] [`proto::pre_inc<>`]] [[unary prefix `--`] [`proto::tag::pre_dec`] [`proto::pre_dec<>`]] [[unary postfix `++`] [`proto::tag::post_inc`] [`proto::post_inc<>`]] [[unary postfix `--`] [`proto::tag::post_dec`] [`proto::post_dec<>`]] [[binary `<<`] [`proto::tag::shift_left`] [`proto::shift_left<>`]] [[binary `>>`] [`proto::tag::shift_right`] [`proto::shift_right<>`]] [[binary `*`] [`proto::tag::multiplies`] [`proto::multiplies<>`]] [[binary `/`] [`proto::tag::divides`] [`proto::divides<>`]] [[binary `%`] [`proto::tag::modulus`] [`proto::modulus<>`]] [[binary `+`] [`proto::tag::plus`] [`proto::plus<>`]] [[binary `-`] [`proto::tag::minus`] [`proto::minus<>`]] [[binary `<`] [`proto::tag::less`] [`proto::less<>`]] [[binary `>`] [`proto::tag::greater`] [`proto::greater<>`]] [[binary `<=`] [`proto::tag::less_equal`] [`proto::less_equal<>`]] [[binary `>=`] [`proto::tag::greater_equal`] [`proto::greater_equal<>`]] [[binary `==`] [`proto::tag::equal_to`] [`proto::equal_to<>`]] [[binary `!=`] [`proto::tag::not_equal_to`] [`proto::not_equal_to<>`]] [[binary `||`] [`proto::tag::logical_or`] [`proto::logical_or<>`]] [[binary `&&`] [`proto::tag::logical_and`] [`proto::logical_and<>`]] [[binary `&`] [`proto::tag::bitwise_and`] [`proto::bitwise_and<>`]] [[binary `|`] [`proto::tag::bitwise_or`] [`proto::bitwise_or<>`]] [[binary `^`] [`proto::tag::bitwise_xor`] [`proto::bitwise_xor<>`]] [[binary `,`] [`proto::tag::comma`] [`proto::comma<>`]] [[binary `->*`] [`proto::tag::mem_ptr`] [`proto::mem_ptr<>`]] [[binary `=`] [`proto::tag::assign`] [`proto::assign<>`]] [[binary `<<=`] [`proto::tag::shift_left_assign`] [`proto::shift_left_assign<>`]] [[binary `>>=`] [`proto::tag::shift_right_assign`] [`proto::shift_right_assign<>`]] [[binary `*=`] [`proto::tag::multiplies_assign`] [`proto::multiplies_assign<>`]] [[binary `/=`] [`proto::tag::divides_assign`] [`proto::divides_assign<>`]] [[binary `%=`] [`proto::tag::modulus_assign`] [`proto::modulus_assign<>`]] [[binary `+=`] [`proto::tag::plus_assign`] [`proto::plus_assign<>`]] [[binary `-=`] [`proto::tag::minus_assign`] [`proto::minus_assign<>`]] [[binary `&=`] [`proto::tag::bitwise_and_assign`] [`proto::bitwise_and_assign<>`]] [[binary `|=`] [`proto::tag::bitwise_or_assign`] [`proto::bitwise_or_assign<>`]] [[binary `^=`] [`proto::tag::bitwise_xor_assign`] [`proto::bitwise_xor_assign<>`]] [[binary subscript] [`proto::tag::subscript`] [`proto::subscript<>`]] [[ternary `?:`] [`proto::tag::if_else_`] [`proto::if_else_<>`]] [[n-ary function call] [`proto::tag::function`] [`proto::function<>`]]][endsect][/============================================================================][section:expression_introspection Expression Introspection: Defining a Grammar][/============================================================================]Expression trees can have a very rich and complicated structure. Often, youneed to know some things about an expression's structure before you can processit. This section describes the tools Proto provides for peering inside anexpression tree and discovering its structure. And as you'll see in latersections, all the really interesting things you can do with Proto begin righthere.[/===============================================][section:patterns Finding Patterns in Expressions][/===============================================]Imagine your DSEL is a miniature I/O facility, with iostream operationsthat execute lazily. You might want expressions representing input operationsto be processed by one function, and output operations to be processed by adifferent function. How would you do that?The answer is to write patterns (a.k.a, /grammars/) that match the structureof input and output expressions. Proto provides utilities for defining thegrammars, and the _matches_ template for checking whether a given expressiontype matches the grammar.First, let's define some terminals we can use in our lazy I/O expressions: proto::terminal< std::istream & >::type cin_ = { std::cin }; proto::terminal< std::ostream & >::type cout_ = { std::cout };Now, we can use `cout_` instead of `std::cout`, and get I/O expression treesthat we can execute later. To define grammars that match input and outputexpressions of the form `cin_ >> i` and `cout_ << 1` we do this: struct Input : proto::shift_right< proto::terminal< std::istream & >, proto::_ > {}; struct Output : proto::shift_left< proto::terminal< std::ostream & >, proto::_ > {};We've seen the template `proto::terminal<>` before, but here we're using itwithout accessing the nested `::type`. When used like this, it is a very simplegrammar, as are `proto::shift_right<>` and `proto::shift_left<>`. The newcomerhere is `_` in the `proto` namespace. It is a wildcard that matches anything.The `Input` struct is a grammar that matches any right-shift expression thathas a `std::istream` terminal as its left operand.We can use these grammars together with the _matches_ template to query atcompile time whether a given I/O expression type is an input or outputoperation. Consider the following: template< typename Expr > void input_output( Expr const & expr ) { if( proto::matches< Expr, Input >::value ) { std::cout << "Input!\n"; } if( proto::matches< Expr, Output >::value ) { std::cout << "Output!\n"; } } int main() { int i = 0; input_output( cout_ << 1 ); input_output( cin_ >> i ); return 0; }This program prints the following:[preOutput!Input!]If we wanted to break the `input_output()` function into two functions, onethat handles input expressions and one for output expressions, we can use`boost::enable_if<>`, as follows: template< typename Expr > typename boost::enable_if< proto::matches< Expr, Input > >::type input_output( Expr const & expr ) { std::cout << "Input!\n"; } template< typename Expr > typename boost::enable_if< proto::matches< Expr, Output > >::type input_output( Expr const & expr ) { std::cout << "Output!\n"; }This works as the previous version did. However, the following does not compileat all: input_output( cout_ << 1 << 2 ); // oops!What's wrong? The problem is that this expression does not match our grammar.The expression groups as if it were written like `(cout_ << 1) << 2`. It willnot match the `Output` grammar, which expects the left operand to be aterminal, not another left-shift operation. We need to fix the grammar.We notice that in order to verify an expression as input or output, we'll needto recurse down to the bottom-left-most leaf and check that it is a`std::istream` or `std::ostream`. When we get to the terminal, we must stoprecursing. We can express this in our grammar using _or_. Here are the correct`Input` and `Output` grammars: struct Input : proto::or_< proto::shift_right< proto::terminal< std::istream & >, proto::_ > , proto::shift_right< Input, proto::_ > > {}; struct Output : proto::or_< proto::shift_left< proto::terminal< std::ostream & >, proto::_ > , proto::shift_left< Output, proto::_ > > {};This may look a little odd at first. We seem to be defining the `Input` and`Output` types in terms of themselves. This is perfectly OK, actually. Atthe point in the grammar that the `Input` and `Output` types are being used,they are /incomplete/, but by the time we actually evaluate the grammar with_matches_, the types will be complete. These are recursive grammars, andrightly so because they must match a recursive data structure!When the `Output` grammar is evaluated against an expression like`cout_ << 1 << 2`, the first alternate of the _or_ is tried first. It will fail,because the expression `cout_ << 1 << 2` does not match the grammar`proto::shift_left< proto::terminal< std::ostream & >, proto::_ >`. Then the second alternate is tried. We match the expression against`proto::shift_left< Output, proto::_ >`. The expression is a left-shift, so we trythe operands. The right operand `2` matches `proto::_` trivially. To see if the left operand `cout_ << 1` matches `Output`, we must recursively evaluate the `Output` grammar. This time we succeed, because `cout_ << 1` will match the first alternate of the _or_. We're done -- the grammar matches successfully.[endsect][/===========================================][section Fuzzy and Exact Matches of Terminals][/===========================================]The terminals in an expression tree could be const or non-const references, orthey might not be references at all. When writing grammars, you usually don'thave to worry about it because _matches_ gives you a little wiggle room whenmatching terminals. A grammar such as `proto::terminal<int>` will match aterminal of type `int`, `int &`, or `int const &`.You can explicitly specify that you want to match a reference type. If you do,the type must match exactly. For instance, a grammar such as`proto::terminal<int &>` will only match an `int &`. It will not match an `int`or an `int const &`.The table below shows how Proto matches terminals. The simple rule is: if youwant to match only reference types, you must specify the reference in yourgrammar. Otherwise, leave it off and Proto will ignore const and references.[table proto::matches<> and Reference / CV-Qualification of Terminals [[Terminal] [Grammar] [Matches?]] [[T] [T] [yes]] [[T &] [T] [yes]] [[T const &] [T] [yes]] [[T] [T &] [no]] [[T &] [T &] [yes]] [[T const &] [T &] [no]] [[T] [T const &] [no]] [[T &] [T const &] [no]] [[T const &] [T const &] [yes]]]This begs the question: What if you want to match an `int`, but not an `int &`or an `int const &`? For forcing exact matches, Proto provides the _exact_template. For instance, `proto::terminal< proto::exact<int> >` would only match an `int` held by value.Proto gives you extra wiggle room when matching array types. Array types matchthemselves or the pointer types they decay to. This is especially useful withcharacter arrays. The type returned by `proto::as_expr("hello")` is`proto::terminal<char const[6]>::type`. That's a terminal containing a6-element character array. Naturally, you can match this terminalwith the grammar `proto::terminal<char const[6]>`, but the grammar`proto::terminal<char const *>` will match it as well, as the followingcode fragment illustrates. struct CharString : proto::terminal< char const * > {}; typedef proto::terminal< char const[6] >::type char_array; BOOST_MPL_ASSERT(( proto::matches< char_array, CharString > ));What if we only wanted `CharString` to match terminals of exactly the type`char const *`? You can use _exact_ here to turn off the fuzzy matching ofterminals, as follows: struct CharString : proto::terminal< proto::exact< char const * > > {}; typedef proto::terminal<char const[6]>::type char_array; typedef proto::terminal<char const *>::type char_string; BOOST_MPL_ASSERT(( proto::matches< char_string, CharString > )); BOOST_MPL_ASSERT_NOT(( proto::matches< char_array, CharString > ));Now, `CharString` does not match array types, only character string pointers.The inverse problem is a little trickier: what if you wanted to match allcharacter arrays, but not character pointers? As mentioned above, theexpression `as_expr("hello")` has the type`proto::terminal< char const[ 6 ] >::type`. If you wanted to match characterarrays of arbitrary size, you could use `proto::N`, which is an array-sizewildcard. The following grammar would match any string literal:`proto::terminal< char const[ proto::N ] >`.Sometimes you need even more wiggle room when matching terminals. Forexample, maybe you're building a calculator DSEL and you want to allow any
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?