Boost logo

Boost :

From: Eric Friedman (ebf_at_[hidden])
Date: 2003-10-20 18:03:45


Brian McNamara wrote:

> On Sun, Oct 19, 2003 at 05:27:24PM -0700, Eric Friedman wrote:
>
>>This is something I had been working on about two months ago but didn't
>>post to the sandbox until recently.
>
> This looks potentially cool, but I don't quite understand it.

Thanks. Hopefully I can clarify...

>>The basic idea is this:
>>
>> switch_( [variant] )
>> |= case_< [pattern1] >(...)
>> |= case_< [pattern2] >(...)
>> :::
>> |= default_(...) // optional catch-all
>> ;
>>
>>The switch_ will fail to compile if not every case is handled. In terms
>
> Given the general form of patterns you describe below, I can't possibly
> imagine how you determine if every case is handled, but for now I'll
> take your word for it. :)

OK :)

>>of handling, the case_ constructors take typical function objects,
>>though the switch_ ignores any return values.
>
> Why ignore return values? If all cases had the same return type (or
> returns types convertible to one fixed type), could you make the whole
> thing an expression? Like
>
> int x = switch_( [variant] ) // or maybe switch_<int>( [variant] )
> |= case_< [pattern1] >(...)
> |= case_< [pattern2] >(...)
> :::
> |= default_(...) // optional catch-all
> ;
>
> (The alternate syntax in the comment suggests that you pass the intended
> return type of the whole expression as an argument to switch_, rather
> than try to infer it from all the case arms.)

This should be workable, and I'll look into it. On first glance, I
prefer the second syntax (i.e., switch_<int>); otherwise I believe the
switch would need to prefer the result type of the first case. I think
the first syntax would also cause some difficulty with lambda expressions.

>>An example usage is the following (though I have again left out the
>>function objects that need to be passed to the case_ constructors):
>>
>> using namespace boost::type_switch;
>>
>> variant<
>> int
>> , pair<int,double>
>> , pair<int,int>
>> , pair<double,int>
>> > var;
>>
>> switch_(var)
>> |= case_< pair<_1,_1> >(...) // matches pair<int,int>
>> |= case_< pair<int,_> >(...) // matches pair<int,*>
>> |= case_< pair<_,_> >(...) // matches pair<*,*>
>> |= case_< int >(...)
>> ;
>
>
> So to make sure I understand, inside the ellipsis here:
>
> |= case_< pair<_1,_1> >(...) // matches pair<int,int>
>
> I'd put a function object that knows how to accept pairs and do
> something? Like "pp" in
>
> struct PairPrinter {
> template <class T, class U>
> void operator()( pair<T,U> p ) const {
> cout << p.first << p.second << endl;
> }
> } pp;
>
> that code?

Yes, that's the idea. Note that lambda functions work, too.

>>The pattern matching is implemented in terms of lambda_match, which I
>>have also added to the sandbox (to boost/mpl). Notably, lambda_match
>>leverages the MPL lambda workarounds for deficient compilers, extending
>>the applicability of type_switch somewhat.
>
>
> Does the pattern matching only know about a fixed set of types (like
> pair)? Or does it work on any template?

Assuming a conformant compiler, pattern matching works on any template
whose arity is under some implementation-defined limit (currently tied
to the MPL preprocessor symbol BOOST_MPL_METAFUNCTION_MAX_ARITY, which
is 5). With true variadic template parameter lists, this arity
restriction could be removed.

On a compiler without partial specialization and template template
parameters, it works only on templates that have the appropriate MPL
lambda_support workarounds (i.e., very few templates). Again, maximum
arity is limited.

Eric


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk