Boost logo

Boost :

From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2007-04-27 16:44:20


On 4/27/07, Peter Dimov <pdimov_at_[hidden]> wrote:
> Daniel Walker wrote:
> > On 4/27/07, Peter Dimov <pdimov_at_[hidden]> wrote:
>
> >> Yes, this is one of the reasons why boost::bind( ... ) == 5 is
> >> valid, but _1 == 5 is not (the other being that given placeholder
> >> interoperability, both Bind and Lambda will claim _1 == 5 as their
> >> own.)
> >
> > Right. As long as lambda has it's own placeholders you'll need to
> > disambiguate them with namespace qualification or using directives.
>
> No, this wasn't what I had in mind.
>
> Consider what would happen if boost::bind supplies a "proper" operator==( a,
> b ) that attempts to return bind<bool>( __equal_to(), a, b ) and makes it
> work not just when a or b is a bind() expression (as is the case today), but
> for placeholders as well.
>
> Lambda also has its operator== which does a similar thing, except that it
> returns a lambda object.
>
> If Lambda starts recognizing Bind's placeholders as its own, the two
> operator== overloads (if visible) will both match ::_1 == 5. A similar
> ambiguity would occur with ll::_1 == 5 if Bind recognizes ll::_1 as a
> placeholder (which it already does).

OK, I see what you mean. That is an interesting problem. You're
talking about a delayed equality comparison that would be invoked like
(_1 == _2)(a, b) or (_1 == 5)(a), essentially providing some
lambda-expression-like functionality in Boost.Bind. There would also
be a problem if standard library implementers made their placeholders
EqualityComparable, like _1 == _2 to distinguish placeholder _1 from
_2 and not the arguments bound to placeholders _1 and _2. This idea
might occur to some vendors and seem like a good one, but it would
break lambda expressions.

This gets to the more general problem of combining objects with
overloaded operators in the same expression. Boost.Parameter has the
same problem when operator|| is used with a lambda expression. As Dave
pointed out a while back, one way around this problem is to "unlambda"
a subexpression when you want lambda's operators to stop being
applied. However, if the expression containing a call to unlambda also
contains placeholders, you're back where you started.

I haven't worked out the details yet, but I think you could make a
function analogous to unlambda except it would only apply to
placeholders and would tell lambda to ignore the placeholder, i.e. to
decline to use the placeholder to hold the place of an argument and
give 3rd-parties the oportunity to use the placeholder. You could call
the function lambda::release_place or just lambda::release. There
could be an inverse lambda::hold function that would force the
placeholder to be used by lambda. If these functions generated proxies
that would disable or enable lambda's operator overloads and if the
proxy generated by lambda::release was recognizable as a placeholder
by 3rd-parties (either by convertibility to TR1 placeholders or a
specialization of is_placeholder) ... then these functions could
disambiguate the expression and let the user specify who gets to use
the placeholder, lambda or someone else. It could look like ...

_1 == _2 // ambiguous
lambda::hold(_1) == _2 // lambda expression
lambda::release(_1) == _2 // 3rd-party expression

Of course, this is only necessary when TR1 implementations or
3rd-parties overload operators for placeholders, and they're used in
conjunction with lambda expressions. Still, you're bringing up an
important point for placeholder interoperability. I may be able to
give a snippet of code for lambda::release tomorrow. lambda::hold is
easy. For _1 it might look like ...

template<class T>
typename enable_if<
    mpl::equal_to<
        mpl::int_<std::tr1::is_placeholder<T>::value>
      , mpl::int_<1>
>
  , lambda::placeholder1_type
>::type
hold(T)
{
    return lambda::_1;
}

Daniel


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