![]() |
Home | Libraries | People | FAQ | More |
The terminals in an expression tree could be const or non-const references,
or they might not be references at all. When writing grammars, you usually
don't have to worry about it because proto::matches<>
gives you a little wiggle room when matching terminals. A grammar such
as proto::terminal<int>
will match a terminal 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 you want to match only reference types, you must specify the reference in your grammar. Otherwise, leave it off and Proto will ignore const and references.
Table 1.7. 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 proto::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
match themselves or the pointer types they decay to. This is especially
useful with character arrays. The type returned by proto::as_expr("hello")
is proto::terminal<char const[6]>::type
. That's a terminal containing
a 6-element character array. Naturally, you can match this terminal with
the grammar proto::terminal<char const[6]>
,
but the grammar proto::terminal<char const *>
will match it as well, as the following code 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 proto::exact<>
here to turn off
the fuzzy matching of terminals, 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
all character arrays, but not character pointers? As mentioned above,
the expression as_expr("hello")
has the type proto::terminal< char const[ 6 ] >::type
. If you wanted to match character
arrays of arbitrary size, you could use proto::N
,
which is an array-size wildcard. The following grammar would match any
string literal: proto::terminal< char const[ proto::N ] >
.
Sometimes you need even more wiggle room when matching terminals. For
example, maybe you're building a calculator EDSL and you want to allow
any terminals that are convertible to double
.
For that, Proto provides the proto::convertible_to<>
template. You can use it as: proto::terminal< proto::convertible_to< double
> >
.
There is one more way you can perform a fuzzy match on terminals. Consider
the problem of trying to match a std::complex<>
terminal. You can easily match
a std::complex<float>
or a std::complex<double>
,
but how would you match any instantiation of std::complex<>
? You can use proto::_
here to solve this problem. Here is the grammar to match any std::complex<>
instantiation:
struct StdComplex : proto::terminal< std::complex< proto::_ > > {};
When given a grammar like this, Proto will deconstruct the grammar and the terminal it is being matched against and see if it can match all the constituents.