Why not overloading && and || in bind?

Hello everyone Is there a reason why boost::bind does not overload the logical operators || and &&? They definitively would come in more handy to build up complicated logical expressions than using the std::logical_and and std::logical_or function objects. Adding... BOOST_BIND_OPERATOR( ||, logical_or ) BOOST_BIND_OPERATOR( &&, logical_and ) ...to the operator definitions in bind.hpp works as expected with the code below (tested with MSVC 2008). So - why not? :-) Regards James ////////////////////////////////////////////////// // Code example: ////////////////////////////////////////////////// struct employee { employee(int id, const string &name) : id(id), name(name) {} int id; string name; }; ostream &operator<<(ostream &o, const employee &e) { o << e.id << " - " << e.name << endl; return o; } typedef vector<employee> emp_container; emp_container staff; staff.reserve(4); staff.push_back(employee(1, "John")); staff.push_back(employee(2, "Paula")); staff.push_back(employee(3, "Sara")); staff.push_back(employee(4, "Geroge")); emp_container::const_iterator e = find_if(staff.begin(), staff.end(), bind(&employee::name, _1) == "Paula" || bind(&employee::name, _1) == "John" || bind(&employee::id, _1) >= 4 ); if (e != staff.end()) { cout << "Found: " << *e << endl; }

At 8:12 PM +0100 2/14/08, Jean-Pierre Bergamin wrote:
Hello everyone
Is there a reason why boost::bind does not overload the logical operators || and &&? They definitively would come in more handy to build up complicated logical expressions than using the std::logical_and and std::logical_or function objects.
My guess is that since boost::bind could not replicate the behavior of && and ||, the authors decided not to provide them. The built in && will not evaluate the second argument if the first is false. Similarly, the built in || will not evalulate the second argument if the first is true. You can write if ( p != NULL && p->someField == 3 ) confident in the knowledge that "p->someField" will not be read from if p == NULL. -- -- Marshall Marshall Clow Idio Software mailto:marshall@idio.com It is by caffeine alone I set my mind in motion. It is by the beans of Java that thoughts acquire speed, the hands acquire shaking, the shaking becomes a warning. It is by caffeine alone I set my mind in motion.

Hello
Gesendet: Donnerstag, 14. Februar 2008 20:44
At 8:12 PM +0100 2/14/08, Jean-Pierre Bergamin wrote:
Is there a reason why boost::bind does not overload the logical operators || and &&? They definitively would come in more handy to build up complicated logical expressions than using the std::logical_and and std::logical_or function objects.
My guess is that since boost::bind could not replicate the behavior of && and ||, the authors decided not to provide them.
The built in && will not evaluate the second argument if the first is false. Similarly, the built in || will not evalulate the second argument if the first is true.
You can write if ( p != NULL && p->someField == 3 ) confident in the knowledge that "p->someField" will not be read from if p == NULL.
Thats right. But this is not really a problem of bind, but of the semantics of the function objects std::logical_and and std::logical_or itself. The second argument is always evaluated. So IMHO the programmer should be aware of that anyway when using those logical expressions. int *p_int_null = NULL; bool result = p_int_null != NULL && *p_int_null > 10; // ok logical_and<bool> and_op; result = and_op( p_int_null != NULL, *p_int_null > 10 ); // Oops Could there be a way to provide a function object safe_logical_and that only evaluates the second argument if the first one is true? Regards Jean-Pierre Bergamin

On Fri, Feb 15, 2008 at 3:14 AM, Jean-Pierre Bergamin
Could there be a way to provide a function object safe_logical_and that only evaluates the second argument if the first one is true?
I've written something like that before as:
// warning, code typed in e-mail
template

On Friday 15 February 2008 17:07:21 Michael Fawcett wrote:
On Fri, Feb 15, 2008 at 3:14 AM, Jean-Pierre Bergamin
wrote: Could there be a way to provide a function object safe_logical_and that only evaluates the second argument if the first one is true?
I've written something like that before as:
// warning, code typed in e-mail template
struct and_ { F0 first; F1 second; and_(F0 f0, F1, f1) : first(f0), second(f1) { }
bool operator()() const { return first() && second(); } };
but if you needed function arguments that weren't available to be bound at and_ construction time you'd have to do some more trickery. This would also scale well using variadic templates.
I don't understand how this code does not evaluate the second argument if the first one isn't true (the previous poster explicitely requested that). When you construct the above functor evaluation of the expression given for "f1" argument will happen which may run into UB, ie the classical "if (p && p->flag)" idiom is broken (and you seem to recognize that... no idea then what you ment with the code). The only way to do it as I can think of it is using some kind of "lazy evaluation" technique. In order to still use similar syntax (again taking the "if (p && p->flag)") one could do something with operator overloading (like boost.lambda overloads as many operators as it can to construct the lambda functors using familiar syntax). Basically something like "evaluate expression p->flag later", but the problem is you won't be able to say "flag" as an expression, you will probably in such a case have to give "p" and a pointer to a member value (for "flag") and have the functor "lazy evaluate" using p->*flagptr which is not all the time (because of overloading -> or ->*) the same as p->flag (if flagptr is the member pointer of flag). I am also interested to see other/better solutions. -- Mihai RUSU Email: dizzy@roedu.net "Linux is obsolete" -- AST

On Fri, Feb 15, 2008 at 10:19 AM, dizzy
On Friday 15 February 2008 17:07:21 Michael Fawcett wrote:
On Fri, Feb 15, 2008 at 3:14 AM, Jean-Pierre Bergamin
wrote: Could there be a way to provide a function object safe_logical_and that only evaluates the second argument if the first one is true?
I've written something like that before as:
<snip code with typos>
but if you needed function arguments that weren't available to be bound at and_ construction time you'd have to do some more trickery. This would also scale well using variadic templates.
I don't understand how this code does not evaluate the second argument if the first one isn't true (the previous poster explicitely requested that). When you construct the above functor evaluation of the expression given for "f1" argument will happen which may run into UB, ie the classical "if (p && p->flag)" idiom is broken (and you seem to recognize that... no idea then what you ment with the code).
I might be showing my misunderstanding of all the intricacies at work here...
Maybe I am not understanding his intents, or maybe we are talking to
two different points? I have pasted below his original code, modified
to use something like what I posted (and also to have a result when &&
is used).
Are you saying that short-circuit evaluation doesn't happen here, i.e.
the second(t) call happens no matter what?
I thought it was understood that overloading && would have that
effect, and the OP understood that too, which is why he suggested a
safe_logical_and. That means overloading && is out of the question,
which we all seem to agree on, but I am feeling a little silly trying
to figure out why the below code is rubbish.
#include "boost/bind.hpp"
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
using namespace boost;
struct employee {
employee(int id, const string &name) :
id(id), name(name) {}
int id;
string name;
};
ostream &operator<<(ostream &o, const employee &e) {
o << e.id << " - " << e.name << endl;
return o;
}
template

Hello On Friday 15 February 2008 18:24:26 Michael Fawcett wrote:
Are you saying that short-circuit evaluation doesn't happen here, i.e. the second(t) call happens no matter what?
At the time of safe_and or safe_or construction both expressions are evaluated and I didn't thought of it more (like using bind and such in your code).
emp_container::const_iterator e = find_if(staff.begin(), staff.end(), safe_logical_and(safe_logical_or(bind(&employee::name, _1) == "Paula", bind(&employee::name, _1) == "John"),
With this kind of usage it is probably fine as bind (and the lambda operator== which I assume it is used in that expression) delays the actual evaluation of the expression (ie of "name" member being equal to some value) until later when you invoke it in your or_/and_ functors. Thanks for taking your time to explain this, I did not understood it from your previous email. -- Mihai RUSU Email: dizzy@roedu.net "Linux is obsolete" -- AST

Marshall Clow:
At 8:12 PM +0100 2/14/08, Jean-Pierre Bergamin wrote:
Hello everyone
Is there a reason why boost::bind does not overload the logical operators || and &&? They definitively would come in more handy to build up complicated logical expressions than using the std::logical_and and std::logical_or function objects.
My guess is that since boost::bind could not replicate the behavior of && and ||, the authors decided not to provide them.
The built in && will not evaluate the second argument if the first is false. Similarly, the built in || will not evalulate the second argument if the first is true.
You are absolutely right. I couldn't think of an elegant way to replicate the behavior of the built-in logical operators at the time I added support for the rest. This thread did spark my interest though and I was able to add && and || to the trunk, complete with the proper short-circuit semantics.

[Please do not mail me a copy of your followup] boost-users@lists.boost.org spake the secret code <011a01c87003$0ff77eb0$6407a80a@pdimov2> thusly:
[...] This thread did spark my interest though and I was able to add && and || to the trunk, complete with the proper short-circuit semantics.
Can you elaborate on the approach you took? I thought it was essentially impossible to replicate the short-circuit semantics with your own user-defined operator|| and operator&&. -- "The Direct3D Graphics Pipeline" -- DirectX 9 draft available for download http://www.xmission.com/~legalize/book/download/index.html Legalize Adulthood! http://blogs.xmission.com/legalize/

Richard:
[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code <011a01c87003$0ff77eb0$6407a80a@pdimov2> thusly:
[...] This thread did spark my interest though and I was able to add && and || to the trunk, complete with the proper short-circuit semantics.
Can you elaborate on the approach you took? I thought it was essentially impossible to replicate the short-circuit semantics with your own user-defined operator|| and operator&&.
It is impossible to overload && in such a way so that in e1 && e2, e2 is not evaluated when e1 is false. But it's possible to overload f && g so that in (f && g)(x) :- f(x) && g(x), g(x) is not evaluated when f(x) is false.

Peter Dimov schrieb:
Richard:
[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code <011a01c87003$0ff77eb0$6407a80a@pdimov2> thusly:
[...] This thread did spark my interest though and I was able to add && and || to the trunk, complete with the proper short-circuit semantics.
Can you elaborate on the approach you took? I thought it was essentially impossible to replicate the short-circuit semantics with your own user-defined operator|| and operator&&.
It is impossible to overload && in such a way so that in e1 && e2, e2 is not evaluated when e1 is false. But it's possible to overload f && g so that in (f && g)(x) :- f(x) && g(x), g(x) is not evaluated when f(x) is false.
Very nice. Your changes do the trick and we hopefully see the changes soon in an official release. Thanks Jean-Pierre Bergamin

[Please do not mail me a copy of your followup] boost-users@lists.boost.org spake the secret code <019801c8721d$e1612fc0$6407a80a@pdimov2> thusly:
It is impossible to overload && in such a way so that in e1 && e2, e2 is not evaluated when e1 is false. But it's possible to overload f && g so that in (f && g)(x) :- f(x) && g(x), g(x) is not evaluated when f(x) is false.
I guess I'm still a little lost... why does it work for f && g and not e1 && e2? Is it because f and g are functors and don't evaluate their expressions until their operator() is called? -- "The Direct3D Graphics Pipeline" -- DirectX 9 draft available for download http://www.xmission.com/~legalize/book/download/index.html Legalize Adulthood! http://blogs.xmission.com/legalize/

Richard:
boost-users@lists.boost.org spake the secret code <019801c8721d$e1612fc0$6407a80a@pdimov2> thusly:
It is impossible to overload && in such a way so that in e1 && e2, e2 is not evaluated when e1 is false. But it's possible to overload f && g so that in (f && g)(x) :- f(x) && g(x), g(x) is not evaluated when f(x) is false.
I guess I'm still a little lost... why does it work for f && g and not e1 && e2? Is it because f and g are functors and don't evaluate their expressions until their operator() is called?
Yes, exactly. As an example, boost::bind( f, 4 ) creates a function object that will call f(4) when its operator() is called; and with the operator&& defined, bind( f, 4 ) && bind( g, 5 ) will call f(4) && g(5) when called, which will correctly not call g(5) when f(4) returns false.

Richard wrote:
[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code <019801c8721d$e1612fc0$6407a80a@pdimov2> thusly:
It is impossible to overload && in such a way so that in e1 && e2, e2 is not evaluated when e1 is false. But it's possible to overload f && g so that in (f && g)(x) :- f(x) && g(x), g(x) is not evaluated when f(x) is false.
I guess I'm still a little lost... why does it work for f && g and not e1 && e2? Is it because f and g are functors and don't evaluate their expressions until their operator() is called?
Yes, and because (f && g) returns another function object, whose operator() can first evaluate f(x) then decide whether to evaluate g(x). Jeff Flinn
participants (7)
-
dizzy
-
Jean-Pierre Bergamin
-
Jeff Flinn
-
legalize+jeeves@mail.xmission.com
-
Marshall Clow
-
Michael Fawcett
-
Peter Dimov