[bind][tr1] patch for more compliant placeholders

Hello, TR1 section 3.6.4 says that placeholders should be default and copy constructible. However, on gcc and borland boost::bind placeholders are not copy constructible. I tried the following C++0x test. #include <boost/tr1/functional.hpp> #include <boost/concept_check.hpp> int main() { boost::function_requires< boost::DefaultConstructible< typeof(std::tr1::placeholders::_1) > >(); boost::function_requires< boost::CopyConstructible< typeof(std::tr1::placeholders::_1) > >(); } This works on gcc 4.1 with GNU's TR1 implementation, but when using Boost.TR1 the second concept check fails to compile. On gcc and borland the placeholders are defined as inline functions. On all other compilers they are objects with internal linkage. From reading the cvs logs for placeholders.hpp, inline function placeholders are useful in order to avoid having data in headers. (Also, apparently gcc may require placeholders to be inline functions to workaround limitations in precompiled headers.) TR1 gets around the data-in-headers issue by specifying that the placeholder declarations in <functional> are extern. So, should Boost.Bind deviate from TR1 bind in terms of the linkage of placeholders or in terms of the set of valid, TR1-compliant programs it can support based on concept requirements? I don't like the idea of requiring Boost.Bind users to link to a library, so neither of these options are particularly attractive. As a compromise, why not allow the user to choose whether placeholders are internal objects or inline functions depending on their needs? The attached patch does this by introducing a macro BOOST_BIND_ENABLE_INLINE_PLACEHOLDERS. When it is defined placeholders are not objects with internal linkage, and their types do not meet the TR1 concept requirements; i.e. they're inline functions. When the macro is not defined placeholders are objects with internal linkage, and their types are both default and copy constructible. The macro is undefined by default. If a user needs inline placeholders for precompiled headers or whatever reason, they can make the trade off and define the macro. The patch also includes user documentation. The patch allows the code above to compile on gcc 4.1 with Boost.TR1, and there are no errors detected when I run the bind test suite under gcc 3.3/4.1 or msvc 7.1/8.0 with and without inline placeholders enabled. Apply with 'patch -p0 < djw_bind_placeholders.patch' from the boost root directory. I hope this is acceptable. Let me know if I'm over looking something. Thanks, Daniel Walker

Daniel Walker wrote:
[...]
I'm not at all sure that breaking the code of all boost::bind users who happen to use precompiled headers with Borland and g++ is acceptable. On first reading, I was inclined to finish the sentence with "... merely in order to make an irrelevant concept check pass". On second reading, you probably have in mind the fact that Lambda takes its arguments by const reference (boost::bind takes them by value and the deduced type of _1 *is* CopyConstructible in this case.) I agree that this is a problem if we want placeholder interoperability under g++ and I'm not sure what is the best way to address the issue. I'm fairly certain that we ought to leave Borland users alone, though.

On 4/26/07, Peter Dimov <pdimov@mmltd.net> wrote:
I agree that breaking code should be avoided if at all possible. And good news! I just ran the test suite with g++ 4.1 using precompiled bind.hpp and mem_fn.hpp and everything passed with object placeholders. What version of g++ had broken PCH and needed the inline placeholders? Inline placeholders could be enabled for that version using BOOST_TESTED_AT. Perhaps, it's better not to mention PCH in the documentation and just make my change based on the compiler's version number. As for Borland, based on your comments in cvs (thanks!), I think this change is innocuous. From Borland's perspective, it's basically just reverting placeholder.hpp back to revision 1.3. I can try to test it with borland 5.5 later. Incidental, if PCH support on g++ is a big priority, some changes should probably be made to the test files in order to run the test suite with precompiled headers. Basically, #include <boost/bind.hpp> more or less needs to be the first line of each file. It may also be a good idea to add PCH set up to the Jamfile since g++ makes you jump through hoops to precompile headers with .hpp extensions.
Actually, what led me down this road is that it's not possible to overload operators for built-in types like functions or function pointers. Lambda overloads operators for placeholders, for example _1++. So, what I'm trying to do with lambda assumes more than the concept requirements in TR1, but presumable most TR1 implementations will use classes or enums for placeholders. Implementations that don't will not be able to use TR1 placeholders in lambda expressions. They'll still have lambda placeholders until the Phoenix merger, I assume, and maybe after that. But independent of my lambda hacking, I didn't see any reason that gcc users (at least for recent versions of gcc) should be forced into using placeholder types that don't meet the TR1 concept requirements. Actually, maybe the TR1 requirements should be broadened in the final ISO draft to explicitly mandate that placeholders be of class or enum types.
I agree that this is a problem if we want placeholder interoperability under g++ and I'm not sure what is the best way to address the issue.
Well, sticking to a standard set of type requirements is a good start towards insuring interoperability, which is why concept checks are useful in testing. I think lambda can work with TR1 placeholders on g++ using GNU's TR1 implementation (and on other compilers using Boost.TR1). It would be a shame if it didn't also work on g++ (and borland) using Boost.TR1 since your original bind implementation is in many ways the mother of them all and one of the primary reasons bind is in TR1 in the first place!
I'm fairly certain that we ought to leave Borland users alone, though.
Like I said, I don't think this change will impact borland users, other than making placeholders more interoperable. I'll try to test it later though. Daniel

Daniel Walker wrote:
I don't remember. We should conservatively assume that 4.0 and below need the workaround. I don't object to us switching to "real" placeholders for 4.1 and later based on your report.
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.)

On 4/27/07, Peter Dimov <pdimov@mmltd.net> wrote:
Great! I'll send another patch that will leave borland and g++ <= 4.0 as they were.
Right. As long as lambda has it's own placeholders you'll need to disambiguate them with namespace qualification or using directives. My goal is to make either choice of placeholders work. Then eventually (perhaps several releases from now) lambda may be able to exclusively support the standard placeholders without requiring its own. Daniel

Daniel Walker wrote:
On 4/27/07, Peter Dimov <pdimov@mmltd.net> wrote:
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).

On 4/27/07, Peter Dimov <pdimov@mmltd.net> wrote:
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
Daniel

Daniel Walker wrote: [...]
I think that the right thing here (if we take the interoperability issue to heart) is to make _1 == _2 "just work". It's an interesting challenge, but it's not impossible since that is exactly what is_bind_expression is intended to solve. The next milestone, boost::bind( f, _1 + _2 ), is harder since it requires boost::bind to respect specializations of is_bind_expression.

Peter Dimov wrote:
Although I don't know all the details of bind and lambda, I had hoped that _1 and _2 would be abstract proto entities, such that _1 == _2 creates an expression template like: equal< terminal< arg<1> >, terminal< arg<2> > > which is a generic, abstract representation of an equals expression with two placeholders. Can't bind and lambda both be implemented to recognize such a type and do the appropriate thing with it? The only minor complication is that for the placeholders and expressions containing them, operator() must evaluate the expression rather than create a larger expression template. But that's trivial to do with proto. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Possibly, if they are made to honor is_bind_expression specializations and result_of, if is_bind_expression reports true for the above type, if the above is a function object that actually returns x == y, and if result_of works for it.
Trivial? :-) Are you assuming variadic templates and decltype? Consider _9 - _4 as one simple example. Either way, if proto does evaluate, there would be no need for most of Lambda.

Eric Niebler wrote:
The part about boost::bind using is_bind_expression and result_of cannot, not without major surgery and possibly compromising portability. Sometimes I get excited about the new C++0x features and tell myself, why not rewrite boost::bind to take advantage of them? Then it occurs to me that C++0x has a std::bind built-in. :-) (Happened twice so far.)

Peter Dimov wrote:
I'll have to take your word for it -- I'm not familiar enough with the TR1 bind to appreciate the difficulty of respecting is_bind_expression. It seems to me that is_bind_expression can be trivially implemented, given appropriately defined placeholders. Eg. if they are defined in a super-secret hidden namespace, like so: namespace super_secret_hidden { template<class I> struct arg { typedef I arity; }; template<class T> yes_type is_bind_expression_impl(T const *); } no_type is_bind_expression_impl(void const *); Now _1 is: proto::terminal<super_secret_hidden::arg<mpl::int_<1> > >::type const _1 = {{}}; And is_bind_expression<> is trivially implementable using ADL and sizeof. Should work for older compilers, too, right? Are their compilers we support that don't do ADL? Result_of support for older compilers ... I suppose that requires partial template specialization, right? Do we still support compilers that don't do PTS?
Haha! -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Implementing is_bind_expression is trivial and done. Making bind respect it is harder. The TR1 spec says that in bind( f, _1 + _2 ); bind needs to look at is_bind_expression<typeof(_1+_2)>::value to determine whether it's a subexpression. This part is not present in boost::bind, it just recognizes its own bind(...) expressions without asking is_bind_expression.
Well, there are various degrees of doing PTS. Borland does PTS in principle, but seems to choke on pretty much every change I make. Pointers to members also stress even the modern compilers - we just found a pm-related bug in VC8. In general after a commit to boost::bind I plan on two platforms breaking right away, one at a later point. :-)

Eric Niebler wrote:
Trying to port this to Borland/CodeGear I found it depends on SFINAE as well. If you try and make sense of the fails I report on the fails I report on the development compiler, you should see all the fails come from awkward cases like pointer-to-cv-qualified-member. This is much better than we can do with the current compiler, where result_of expressions can't even be parsed (function types between anglew brackets) Of course, Borland/CodeGear still can't handle proto, so perhaps this is all accademic? -- AlisdairM

On 4/27/07, Eric Niebler <eric@boost-consulting.com> wrote:
boost::result_of isn't available on compilers where BOOST_NO_SFINAE or BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION are defined. I think that's been the case for a long time. So, I think it's reasonable that if boost::result_of doesn't support those compilers, things that rely on boost::result_of can't be expected to support them either. Daniel

On 4/27/07, Peter Dimov <pdimov@mmltd.net> wrote:
Actually, I think that's a great idea down the road. Boost.Bind can still be useful even after everyone gets std::bind at no additional cost with their compiler (though that might not happen 'til 2010 or so). If the current TR1 bind implementation were moved to Boost.TR1 then Boost.Bind could improve on the standard. One area where it could do better is in adaptability for call wrappers. In TR1 3.3/3 call wrappers with weak result types (the wrappers that bind must generate when the Result template parameter is not explicitly used) are required be un-adaptable (no result_type member) if the bound function object has no result_type member. It could do better if it provided a result<> member that used result_of. To go one step further, if Boost provided a result_of implementation using decltype in addition to a TR1 compliant result_of, Boost.Bind could support perfect, generic, non-intrusive return type deduction. Daniel

Daniel Walker wrote:
std::result_of is required to do the right thing for every function object returned by the standard library, including the result of std::bind. It is not specified whether this is done via a nested result<> or via another mechanism.
Every C++0x compiler is expected to implement result_of in terms of decltype. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2194.pdf

On 4/28/07, Peter Dimov <pdimov@mmltd.net> wrote:
Awesome, I didn't realize that had already happened. That's good news. So, Boost would never need to maintain separate TR1 and TR1++, so to speak ;), result_of and bind implementations. Is this highly likely to go into the final ISO draft? It would be great if everyone got the best possible bind as part of the standard! Daniel

On 4/27/07, Peter Dimov <pdimov@mmltd.net> wrote:
OK, I think you're bring up something new here with is_bind_expression that I haven't thought of yet. But first back to placeholders. _1 == _2 will "just work" as long as lambda is the only party overloading operator==. But if it's not, I don't understand how is_bind_expression would resolve the ambiguity. So, you mean the complete statement would be 'bind(f, _1 == _2);'? If _1 == _2 was unambiguous, and is_bind_expression were specialized for the functor generated by _1 == _2, then bind could treat its second argument as a subbind expression. However, back to your hypothetical, assuming that one day Boost.Bind provided delayed operator evaluation like lambda does now, and both bind's and lambda's operators are visible, and they're both using standard placeholders (or placeholders that specialize is_placeholder), then _1 == _2 is still ambiguous, right? If the idea is that Boost.Bind does *not* overload operators for placeholders, and lambda expressions can be treated as subbind expressions, then yeah, that's a great idea. But even if Boost.Bind doesn't overload operators for placeholders, there's still the issue of using lambda with 3rd-party libraries that do. I can't think of any examples right now, but they may very well start to emerge over the years. If an unrelated library overloads an operator for a placeholder, and it's used in a lambda expression, then both libraries break. So, I think there could still be a use for something like lamda::hold(_1) and lambda::release(_1). Actually, it may be a good idea to make these functions more general so that they could be used to resolve any operator overload ambiguities such as the one with Boost.Parameter where both it and lambda overload operator||, or some other combination of libraries where neither provide something similar to unlambda to remove their operator overloads from the compiler's overload list.
The next milestone, boost::bind( f, _1 + _2 ), is harder since it requires boost::bind to respect specializations of is_bind_expression.
Alright, you're getting me all excited about this idea. I've been trying to work one step at a time, first getting lambda to work with result_of (I have a cleaner/better implementation than my previously submitted patch that incorporates suggestions from the result_of discussion on this list), now I'm trying for TR1 placeholders and then I was going to look into TR1 bind. When I get to bind, I'll try to specialize is_bind_expression for lambda::lambda_functor<>, and I think this will work right away with GNU's TR1 since it already uses is_bind_expression. So, this may not be so far off. Incidentally, currying expressions like ... boost::bind<int>((_1 + _2 + _3), _1, _2, c)(a, b) ... should work as soon as lambda accepts TR1 placeholders. Right now you would have to write ... boost::bind<int>((lambda::_1 + lambda::_2 + lambda::_3), ::_1, ::_2, c)(a, b); Daniel

Daniel Walker wrote:
It won't if you haven't included a lambda header or if you're using a compiler that is supported by Bind, but not by Lambda. The idea of "just works" is that _1 == _2 for Bind placeholders should be resolved by Bind without an ambiguity, yet it should still be recognized by Lambda as a subexpression. _1 + _2, however, should be picked up by Lambda because Bind does not support operator+, only relations and logical not.

On 4/28/07, Peter Dimov <pdimov@mmltd.net> wrote:
So, you mean that Bind is definitely going to overload the relational operators. Is that also under consideration by the standard committee? Why not bring all the operators into bind and remove (or at least largely remove) the need for a separate lambda library to support lambda expressions? Daniel

Daniel Walker wrote:
So, you mean that Bind is definitely going to overload the relational operators.
Bind already overloads the relational operators: http://www.boost.org/libs/bind/bind.html#operators in more limited contexts. It is trivial to extend this overloading to work on expressions that involve no bind calls such as _1 == 5, _1 > _2 or !_4 - provided that the placeholders aren't inline functions, of course. :-)
Is that also under consideration by the standard committee?
Not yet.
The operators that Bind currently supports do not require return type deduction since their return type is assumed to be bool.

On 4/28/07, Peter Dimov <pdimov@mmltd.net> wrote:
Aha, that was the source of my confusion. I'm not so worried about bind(f, _1) < x. This already works even when the lambda header is included. Moving forward, I think breaking this code can be avoided. I'm worried about bind(f, _1 < x) or bind(f, _1) < _2.
It's also trivial to make lambda generate is_bind_expression specialized functors. I think one or the other of the libraries should take care of this use-case but not both, just to avoid the head aches. But again, if one of the libraries provided a hold function to claim the placeholder it could resolve the problem.
Is that also under consideration by the standard committee?
Not yet.
Then this is TR1++. ;-) Which is fine by me, actually. I would like to have a one-stop-shop for all my on-fly, unnamed-function-creation needs. But I don't want to wait 10 years for it. The past decade of dealing with bind/lambda incompatibility has been irritating enough. I think just making lambda work with the new standard will go along way toward alleviating these inconveniences. In the long run, it would be nice if lambda and bind were folded together though.
That makes sense. Daniel

Daniel Walker wrote:
bind(f, _1) < _2 is also handled by Bind.
This is how I think it should work: boost::bind(...) relop ...: Bind (as today) !boost::bind(...): Bind _1 relop ...: Bind, translates to boost::bind( __relop(), _1, ... ) !_1: Bind, translates to boost::bind( __not(), _1 ) _1 + ...: Lambda (based on is_placeholder<>) ll::bind( f, _1 ): as above boost::bind(...) + ...: Lambda (based on is_bind_expression<>) ll::_1 relop ...: Lambda or Bind - TBD !ll::_1: Lambda or Bind - TBD boost::bind( f, _1 + _2 ): won't work with boost::bind; should work with std::bind.

On 4/28/07, Peter Dimov <pdimov@mmltd.net> wrote:
OK, yes, it works because of the bind expression. Bind's relational operators are overloaded for bind expressions. Got it.
Everything seems fine to me except for one issue. To make sure we're on the same page, I'm going to try to itemize a couple of the tasks that need to be done. Let me know if I miss something.
boost::bind(...) relop ...: Bind (as today)
Task for bind: boost::bind needs to use is_bind_expression in case the second argument to relop is generated by lambda. Other than that, this already works if one argument to relop is a boost::bind expression and the other is either a boost::bind expression or a placeholder. Task for lambda: relop needs to be supplied for std::bind but disabled for boost::bind. is_bind_exression needs to be specialized for lambda_functor so that boost::bind's relop can handle it. lambda needs to accept is_bind_expressions (from either boost::bind, std::bind or a 3rd-party for that matter) so that the whole thing can be composed into larger lambda expressions.
!boost::bind(...): Bind
Task for bind: None. This works as is. Task for lambda: operator! needs to be supplied for std::bind but disabled for boost::bind.
_1 relop ...: Bind, translates to boost::bind( __relop(), _1, ... ) !_1: Bind, translates to boost::bind( __not(), _1 )
If the second argument to relop is a boost::bind expression nothing needs to be done. It works like above. If not there's a problem. Also, for !_1 there's a problem. I'll comment below on the TBD items.
_1 + ...: Lambda (based on is_placeholder<>)
I should have this shortly.
ll::bind( f, _1 ): as above boost::bind(...) + ...: Lambda (based on is_bind_expression<>)
No problem here. This comes next.
ll::_1 relop ...: Lambda or Bind - TBD !ll::_1: Lambda or Bind - TBD
To get these to be handled by Bind instead of lambda for either lambda placeholders or standard placeholders you need to add overloads for placeholders in Boost.Bind, right?. Certainly for !_1. I don't like this idea. This will already work with lambda's new overloads, which need to be there because std::bind doesn't have them. As I tried to illustrate above, the relops for boost::bind expressions don't cause a problem. lambda will provide them for std::bind, and if ADL doesn't take care of it, I can explicitly disable lambda's overloads for boost::bind expressions so that we don't step on each other's toes. I cannot do that for TR1 placeholders because we'll both be using the same types. I can only think of two options if you add relop overloads for placeholders to Bind. 1) I could disable/enable relops for placeholders based on some proxy type generated from a hold or release function. 2) I could provided relops for std::placeholders in general but disable them for boost::bind placeholders specifically like I might have to do for relops used with boost::bind expressions. Let me know if there's a proverbial 3rd way. Regardless, it would be much easier (and less of a burden on users) if you just didn't add operator overloads for placeholders to begin with. Or if you added all operators to Boost.Bind and got rid of lambda entirely. The thing is that it's confusing now for people to remember when to use boost::bind and when to use lambda::bind. I think having duplicate operators for placeholders will only compound the problem. And now there's a third bind, std::bind, that behaves differently from the other two with regard to relop expressions, and users have to keep track of that. Actually, it could be a massive and unwelcome surprise if someone using Boost.TR1 for portability reasons switched to a new platform that didn't have TR1 and found that some of the relops were suddenly being delayed. Not that that's likely, but it may be possible. What I would like to see is for users to be able to think ... "I need to delay a function call" - go to std::bind (no need to consider lambda for this ever again) "OK, now I need to curry the function with placeholders" - go to std::placeholders (no need to consider lambda for this ever again) "So far so good. Now I need to compose the function in an expression with operators" - go to boost::lambda (no need to worry about qualifying bind, _1, _2, etc. or what expressions are supported by one and not the other. no more nonsense.) "Great, that was easy" - go home and take a nap One day boost::bind could be rewritten to support function composition from operator expressions like lambda or there could be a 3-way merge with Phoenix and Proto could help things along. That would be great and lambda could be made obsolete entirely. If it made it into some future standard, say C++1x, all the better. For now (i.e. Boost 1.35), I think it might clarify things if there were a delineation of functionality between bind and lambda with bind supporting delayed-calls, currying, and composition for functions and lambda supporting delayed-calls, curing and composition for expressions with operators. And both can be combined together freely, intuitively and portably. Also, this should be done without breaking old code that relies on lambda's bind or placeholders or boost::bind's relop overloads. Note that the only reason to keep lambda::bind, lambda::_1, etc. is too avoid breaking existing code that qualifies the names, and that's only for a release or two assuming lambda's bind and placeholders are deprecated. With TR1, they're definitely no longer needed. Future code should never have to deal with these issues again because the standard bind and placeholders can just be used.
boost::bind( f, _1 + _2 ): won't work with boost::bind; should work with std::bind.
This won't work for boost::bind until it supports is_bind_expression; afterwards it should be fine, right? I guess that's it. Oh, one more thing, while I've been working on result_of I've written about umpteen million test assertions for the various operators, other lambda functions for construction, unlambda, embedded stl algorithms, etc. with various function arities to make sure that I didn't miss an expression and that they can all be used with result_of. I think I have good coverage and plan on reusing these tests for TR1 placeholders and bind. So, it should give us some confidence that the changes work. Thanks! Daniel

Daniel Walker wrote:
I'm planning to propose std::bind relational operators and operator! for the next meeting. They may or may not actually go in, of course. I'm still not sure that I have the design 100% right, though. The basic idea is to add template<class L, class R> typename enable_if< is_bind_expression<L>::value || is_placeholder<L>::value || is_bind_expression<R>::value || is_placeholder<R>::value, ...>::type operator==( L const & l, R const & r ) { return bind( __equal_to(), l, r ); } where __equal_to()(x,y) returns x == y. The interesting issue is in which namespace we need to add the above. The global namespace works most of the time, is contrary to the spirit of the rest of the standard, and fails when an operator== overload in the current scope hides the global one. Namespace std is in line with the rest of the standard, but doesn't work for the placeholders via ADL, since these are defined in namespace std::placeholders. In other words, _1 == 5 will fail by default unless there is a "using namespace std" directive in effect. The best solution I have so far is to define the operators in std, move the placeholders into std as well and import them into std::placeholders with using declarations. This preserves the original use case of "using namespace std::placeholders" making only _K visible, while still keeping std as an associated namespace for ADL purposes, so that _1 == 5 finds the above std::operator==. It should probably be noted that this will fail if Herb Sutter's "Fixing ADL" proposal is accepted. This is also an interesting use case for a || concept requirement. I'm not terribly familiar with the concepts proposal though; it might offer an alternative solution that I don't know about.

On 4/29/07, Peter Dimov <pdimov@mmltd.net> wrote:
How about this. Dumping operator== into the global namespace is bad. Dumping it into a namspece with a type that you're making EqualityComparible (thus enabling ADL) is normal. But if operator== no longer means EqualityComparible and instead means delayed evaluation, then I think that's not a good idea. I like something similar to lambda's approach of putting the operators in a separate namespace. The user enables delayed evaluation of operators by importing them via a using directive. So, let's consider a namespace separated from types that may or may not be EqualityComparible (i.e. separated from both std and std::placeholders). The sole purpose of this namespace is to provide an interface for users to enable bind expressions from operators. So, let's call it std::bind_expressions, or if it's not too terse, just std::expressions. So the user could do something like. using std::bind; // enable delayed functions using std::placeholders; // enable currying using std::expressions; // enable delayed operator expressions Now, consider a user who wants to make functors equality comparable, perhaps because some are multithreaded and need to be treated differently than others. The user wants to be able to compare user defined functors and std functors (even functors resulting from std::bind) transparently. So, the user defines generic operator== overloads in the namespace user::comparison. Now, the user can do ... user::user_functor f; { using namespace user::comparison; f == f; // user comparison is true f == std::tr1::bind(f); // user comparison is false } If the user instead wanted to delay the comparison using bind (with namespace expressions in tr1 for notational consistence) the code above would change to ... { using namespace user::comparison; f == f; // user comparison is true } { using namespace std::tr1::expressions; f == std::tr1::bind(f); // delay == } If the user wanted to be able to delay and compare, any delayed expression operator can be imported individually ... { using namespace user::comparison; f == f; // user comparison is true { using std::tr1::expressions::operator==; f == std::tr1::bind(f); // delay == } f == std::tr1::bind(f); // user comparison is false } This would work similarly for placeholders. Now, if the user had defined the comparison operators in namespace user instead of namespace user::comparison then the statements where delayed expressions were in scope would be ambiguous due to ADL. However, this gets back to the more general problem of combining types with the same overloaded operators in the same expressions. You can't override ADL by qualifying the scope of an operator like 'x std::tr1::expression::== y'. You could say 'std::tr1::expression::operator==(x, y)', but that gets cumbersome. A general mechanism for telling an operator to claim an argument like std::tr1::expression::hold(x) == y could do the trick, but this is also kind of cumbersome. I'm not familiar with Herb Sutter's ADL proposal, so I don't know if this would help matters. Still, I think putting the operators in a separate namespace is a fair trade. Users can define bind compatible relops or ADL compatible relops for their types but not both without requiring cumbersome syntax like std::tr1::expression::operator==(x, y); i.e if they want the easy syntax they have to chose one or the other. I think this is reasonable because bind relop likely has completely different semantics than any user defined relop found via ADL.
What do you mean by 'a || concept requirement'? I'm not sure that any concept would help in my example above. What might help is namespace qualification for scope operators. Below, there's a complete example using the code snippets if you want to copy, past and tweak. It needs a tr1 compliant standard library. I'm not sure, but I think Boost.TR1 won't work because the operators are already defined in namespace boost. I compiled with ... g++ -I/usr/include/c++/4.1/tr1 file.cpp Daniel #include <functional> #include <boost/mpl/int.hpp> #include <boost/mpl/logical.hpp> #include <boost/type_traits.hpp> #include <boost/utility/enable_if.hpp> namespace std { namespace tr1 { template<class F, class A0, class A1> class bind_expression { static F f; static A0 a0; static A1 a1; public: typedef typeof(bind(f, a0, a1)) type; }; namespace expressions { template<class L, class R> typename boost::enable_if< boost::mpl::or_< boost::mpl::int_<is_bind_expression<L>::value> , boost::mpl::int_<is_bind_expression<R>::value> > , typename bind_expression< equal_to<L>, L, R >::type
}}} // end std namespaces namespace user { class user_functor { int data; public: typedef int result_type; int operator()() { return data; } bool operator==(user_functor const& that) { return this->data == that.data; } }; template<class Functor> struct is_user_functor { static const bool value = false; }; template<> struct is_user_functor<user_functor> { static const bool value = true; }; namespace comparison { template<class L, class R> typename boost::enable_if< boost::mpl::and_< boost::mpl::int_<is_user_functor<L>::value> , boost::is_same<L, R> > , bool
template<class L, class R> typename boost::disable_if< boost::mpl::and_< boost::mpl::int_<is_user_functor<L>::value> , boost::is_same<L, R> > , bool
}} // end user namespaces int main() { user::user_functor f; { using namespace user::comparison; f == f; // user comparison is true f == std::tr1::bind(f); // user comparison is false } { using namespace user::comparison; f == f; // user comparison is true } { using namespace std::tr1::expressions; f == std::tr1::bind(f); // delay == } { using namespace user::comparison; f == f; // user comparison is true { using std::tr1::expressions::operator==; f == std::tr1::bind(f); // delay == } f == std::tr1::bind(f); // user comparison is false } }

Daniel Walker wrote:
Out of curiosity, what are || concepts? I haven't heard of them before. Google gave me nothing.
IIUC, given a pair of concepts A || B, your type must match exactly one of those concepts, and not both. The xor nature of the proposal fooled me, as I naturally thought it would apply to something being 'copyable or moveable' but I don't think it worked out that way. This is at the obscure end of my understanding of Concepts though - so I might well be making silly errors in this summary. -- AlisdairM

On 4/27/07, Daniel Walker <daniel.j.walker@gmail.com> wrote:
I've attached a patch that leaves borland and g++ <= 4.0 as they were. I also changed my little addition to the documentation to reflect this. And one more thing, I couldn't find a macro analogous to BOOST_MSVC for gcc's version number that I could use with BOOST_WORKAROUND. I added one (BOOST_GCC) in boost/config/compilers/gcc.hpp that does the same things as MPL's gcc config file. Let me know if there's some other way this should have been done. Thanks! Daniel

on Fri May 04 2007, "Daniel Walker" <daniel.j.walker-AT-gmail.com> wrote:
Posting it to the SF patch tracker is always a good move. :) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com

Daniel Walker wrote:
I'd like to understand the motivation behind BOOST_BIND_ENABLE_INLINE_PLACEHOLDERS. Other comments: BOOST_WORKAROUND(BOOST_GCC, <= 0x0400) is always true when the compiler isn't GCC since BOOST_GCC is not defined. Depending on the BOOST_GCC macro means that bind.hpp will not work as intended with an empty config.hpp. It did once, and I've been careful to retain this feature... not that it's being tested, or that anyone I know takes advantage of it. :-) How about just using #if defined(__BORLANDC__) || defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ <= 400) ?

On 5/9/07, Peter Dimov <pdimov@mmltd.net> wrote:
I'm not sure if I entirely understand it either. ;-) I was trying to figure out why the placeholders were implemented as functions at all. Apparently, around version 1.5 of placeholders.h, inline functions were introduced for all compilers based on a suggestion from Yitzhak Sapir. I haven't gone back through the list archives to see what was under discussion at that time, but I assumed that an inline function implementation saved some overhead in the .data sections of object files. Or perhaps there was some other reasons that made defining the objects repeatedly in each compilation unit unattractive. I'm not sure how big a deal this is. In TR1 the placeholders are extern, so it's not an issue. But I don't think that extern would be a good idea for boost bind. I figure if at one time it mattered to users like Yitzhak, it may matter to others again and why not give them a choice? But again, I don't fully understand why inline functions were under consideration in the first place. There use as a workaround for gcc's PCH seems to have come up years later.
Oops! Good catch. Thanks!
I just like the idea of being able to search for BOOST_WORKAROUND and find workaround specific code. I think it should be #if defined(__BORLANDC__) || (defined(BOOST_GCC) && BOOST_WORKAROUND(BOOST_GCC, <= 0x0400)) Daniel

On 5/9/07, Daniel Walker <daniel.j.walker@gmail.com> wrote:
Double oops. I take that back. I just remembered I actually tested this last week on msvc, and it worked. If the first argument to BOOST_WORKAROUND is undefined the condition won't be met. There's documentation in boost/detail/workaround.hpp. So, that part of the patch should work as is, only triggering the workaround for the appropriate gcc versions. Daniel

Daniel Walker wrote:
Cool, I'd forgotten about this. :-) There's an additional reason to check with defined() first, though: using an undefined symbol with BOOST_WORKAROUND generates a warning with -Wundef, and there are people who use this flag. This doesn't really apply here since -Wundef is a GCC option and the symbol will be defined, but I still consider it a good practice to qualify any use of BOOST_WORKAROUND with the appropriate defined(). Not triggering -Wundef is another feature that we don't test.

Daniel Walker wrote:
See http://lists.boost.org/Archives/boost/2002/03/26490.php and the associated thread. The specific 1.5 change is caused by Borland PCH problems. 1.6 has probably come right after one regression test cycle. placeholders.hpp has evolved into its current form since many compilers prefer one of the implementations and have problems with the others, especially when using precompiled headers.

On 5/10/07, Peter Dimov <pdimov@mmltd.net> wrote:
OK, so this has always been related to PCH. In that case, if there's no other use for the inline functions there's no reason to complicate matters by allowing users to change the placeholder implementation with a macro. Since the bulk of my patch is adding documentation for that macro it doesn't really save you time. Could just qualify the gcc preprocessor condition for gcc <= 4.0 by hand and commit it to CVS? Daniel

Daniel Walker wrote:
On 5/9/07, Peter Dimov <pdimov@mmltd.net> wrote:
I still prefer the above, sorry. It limits the patch to just placeholders.hpp and is consistent with the rest of its checks. Let's leave the task of defining BOOST_GCC properly to someone else. I see that there is already BOOST_CXX_GNUC in Boost.Config along with a bunch of other BOOST_CXX_ macros, but it seems always zero.

On 5/10/07, Peter Dimov <pdimov@mmltd.net> wrote:
I agree that it's not good to have patches touch files outside the given library. But I know as soon as BOOST_GCC is gone I'll discover that I need it again. If my current approach is unsatisfactory and someone can give me instructions, I'll volunteer for the job and send another patch. Or if my current approach is OK, maybe someone can apply the patch interactively or copy and past that single line into boost/config/compiler/gcc.hpp. As for using BOOST_WORKAROUND in bind, that's your call. What ever your prefer is fine with me, I just want to be able to use the object placeholders in recent gcc versions. ;-) Thanks, Daniel
participants (5)
-
AlisdairM
-
Daniel Walker
-
David Abrahams
-
Eric Niebler
-
Peter Dimov