std::find_if, std::logical_or and short evaluation

M

marco_segurini

Hi,

when the following code is executed

bool Test1();
bool Test2();

....
if (Test1() || Test2()) { ... }
....

Test2 is executed only if Test1 returns false.

I am trying to create a predicate for find_if with the same behaviour:

find_if(iterBegin,iterEnd, Test1 || Test2)


For do this I have modificated the boost::compose_f_gx_hx template in
this way:

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

/* class for the compose_gx_op_hx adapter
*/
template <class OP1, class OP2, class OP3>
class compose_gx_op_hx_t
: public std::unary_function<typename OP2::argument_type,
typename OP1::result_type>
{
private:
OP1 op1; // process: op2(x) op1 op3(x)
OP2 op2;
OP3 op3;
public:
// constructor
compose_gx_op_hx_t (const OP1& o1, const OP2& o2, const OP3& o3)
: op1(o1), op2(o2), op3(o3) {
}

// function call
typename OP1::result_type
operator()(const typename OP2::argument_type& x) const {
return op2(x) || op3(x);
// return op1(op2(x),op3(x));
}
};

/* convenience functions for the compose_gx_op_hx adapter
*/
template <class OP1, class OP2, class OP3>
inline compose_gx_op_hx_t<OP1,OP2,OP3>
compose_gx_op_hx (const OP1& o1, const OP2& o2, const OP3& o3) {
return compose_gx_op_hx_t<OP1,OP2,OP3>(o1,o2,o3);
}

class Test1
: public std::unary_function<int,bool>
{
public:
bool operator()(int) const { std::cout << "Test1" << std::endl;
return true; }
};

class Test2
: public std::unary_function<int,bool>
{
public:
bool operator()(int) const { std::cout << "Test2" << std::endl;
return false; }
};

int main()
{
std::vector<int> vInt(1,1);

std::find_if(vInt.begin(), vInt.end(),
compose_gx_op_hx(std::logical_or<bool>(), Test1(), Test2()));

return 0;
}

The problem is that not a *general* solution because the
logical_operator is hardcoded (|| in this case) even if it is the
first template parameter.

If I use the commented statement
return op1(op2(x),op3(x));
instead of
return op2(x) || op3(x);
both Test1() and Test2() are always called and there is no short
evaluation.

Many thanks for any help.

Marco.
 
V

Victor Bazarov

marco_segurini said:
when the following code is executed

bool Test1();
bool Test2();

...
if (Test1() || Test2()) { ... }
...

Test2 is executed only if Test1 returns false.

I am trying to create a predicate for find_if with the same behaviour:

find_if(iterBegin,iterEnd, Test1 || Test2)


For do this I have modificated the boost::compose_f_gx_hx template in
this way:

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

/* class for the compose_gx_op_hx adapter
*/
template <class OP1, class OP2, class OP3>
class compose_gx_op_hx_t
: public std::unary_function<typename OP2::argument_type,
typename OP1::result_type>
{
private:
OP1 op1; // process: op2(x) op1 op3(x)
OP2 op2;
OP3 op3;
public:
// constructor
compose_gx_op_hx_t (const OP1& o1, const OP2& o2, const OP3& o3)
: op1(o1), op2(o2), op3(o3) {
}

// function call
typename OP1::result_type
operator()(const typename OP2::argument_type& x) const {
return op2(x) || op3(x);
// return op1(op2(x),op3(x));
}
};

/* convenience functions for the compose_gx_op_hx adapter
*/
template <class OP1, class OP2, class OP3>
inline compose_gx_op_hx_t<OP1,OP2,OP3>
compose_gx_op_hx (const OP1& o1, const OP2& o2, const OP3& o3) {
return compose_gx_op_hx_t<OP1,OP2,OP3>(o1,o2,o3);
}

class Test1
: public std::unary_function<int,bool>
{
public:
bool operator()(int) const { std::cout << "Test1" << std::endl;
return true; }
};

class Test2
: public std::unary_function<int,bool>
{
public:
bool operator()(int) const { std::cout << "Test2" << std::endl;
return false; }
};

int main()
{
std::vector<int> vInt(1,1);

std::find_if(vInt.begin(), vInt.end(),
compose_gx_op_hx(std::logical_or<bool>(), Test1(), Test2()));

return 0;
}

The problem is that not a *general* solution because the
logical_operator is hardcoded (|| in this case) even if it is the
first template parameter.

If I use the commented statement
return op1(op2(x),op3(x));
instead of
return op2(x) || op3(x);
both Test1() and Test2() are always called and there is no short
evaluation.

It's a known problem. For example, if you overload operator || or
operator &&, you cannot make them behave like the built-in ones do
(WRT short-cutting or specific order of evaluation or existence of
a sequence point). It's a limitation of the language.

In order to achieve what you want you'd have to basically repeat
what the language defines about the logical operators. You can do
it using specialisations. Instead of making a generic template,
you should probably make two templates, one for OR and one for AND.
The 'OR' will do as you wrote

return op2(x) || op3(x);

and the 'AND' will do

return op2(x) && op3(x);

Otherwise, your solution is declaring "operator traits" with the order
of operand evaluation either predefined or undefined, and then, again,
implement variations of the execution based on the traits:

if (op1::traits::do_second_if_first_false) {
bool x = op2(x);
if (!x)
return op3(x);
else
return x;
}
else if (op1::traits::do_second_if_first_true) {
bool x = op2(x);
if (x)
return op3(x);
else
return false;
}
else
return op1(op2(x), op3(x));

Then you could (optionally) define those traits (or, specialise them)
for 'or' and 'and' and use generic ones for anything else.

Victor
 
J

jota

if (Test1() || Test2()) { ... }
...

Test2 is executed only if Test1 returns false.

I am trying to create a predicate for find_if with the same behaviour:

find_if(iterBegin,iterEnd, Test1 || Test2)

If you are not hooked to the find_if there is another way of implementing
such a behaviour
I once used something like:
---------------
for(LIST::iterator i=l.begin();i!=l.end();i++)
if(pred1(*i) || pred2(*i))
{
//do stuff
}
 
K

Karl Heinz Buchegger

marco_segurini said:
[snip]

instead of
return op2(x) || op3(x);
both Test1() and Test2() are always called and there is no short
evaluation.

ALl of that seems a lot of work for a simple:

bool Test()
{
return Test1() || Test2();
}

find_if( iterBegin, iterEnd, Test )
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top