std::for_each + break

Y

yurec

Hi

I wanna give template method of library class with some predicate,
which I put to std::for_each in that method.How to break the
std::for_each with predicate?(exceptions are used olny in exceptional
situations, as i know)

Thanks
 
A

alan

Hi

I wanna give template method of library class with some predicate,
which I put to std::for_each in that method.How to break the
std::for_each with predicate?(exceptions are used olny in exceptional
situations, as i know)

Thanks

Perhaps you would be better served by std::find_if
 
J

James Kanze

I wanna give template method of library class with some
predicate, which I put to std::for_each in that method.How to
break the std::for_each with predicate?(exceptions are used
olny in exceptional situations, as i know)

You can't. That's precisely the point of for_each.
 
K

Kira Yamato

Hi

I wanna give template method of library class with some predicate,
which I put to std::for_each in that method.How to break the
std::for_each with predicate?(exceptions are used olny in exceptional
situations, as i know)

Thanks

I would think throwing an exception is a simple and elegant solution here.

Just because we have called a mechanism 'exception' and gave it the
connotation that it is for error-handling purposes, does not mean that
we can only use the mechanism as such and nothing else.

Ok, so some runtime cost is needed to setup try-catch blocks, but I'm
not sure scanning the list twice with find_if/for_each combo is
necessarily faster.
 
K

Kai-Uwe Bux

Kira said:
I would think throwing an exception is a simple and elegant solution here.

Simple and elegant? in this case?

Compare

try {
std::for_each( seq.begin(), seq.end(), throwing_predicate_and_action );
}
catch ( whatever ) {}

to

for ( iterator_type iter = seq.begin();
iter != seq.end() && ! break_condition( *iter );
++iter ) {
some_action( *iter );
}

I cannot say that I find the try-throw-catch version easier to grok or more
elegant.

Just because we have called a mechanism 'exception' and gave it the
connotation that it is for error-handling purposes, does not mean that
we can only use the mechanism as such and nothing else.

I wholeheartly agree to that. I prefer to refer to the mechanism as
try-throw-catch or stack-unwinding to avoid connotations. Sometimes, when
someone says "exception are only to be thrown is exceptional
circumstances", I reply (only half-jokingly): "ok. so throw something
else".

Ok, so some runtime cost is needed to setup try-catch blocks, but I'm
not sure scanning the list twice with find_if/for_each combo is
necessarily faster.

No, but a simple for loop might be (and it also might convey intend better
than any of the alternatives).


Best

Kai-Uwe Bux
 
T

terminator

Simple and elegant? in this case?

Compare

try {
std::for_each( seq.begin(), seq.end(), throwing_predicate_and_action );
}
catch ( whatever ) {}

to

for ( iterator_type iter = seq.begin();
iter != seq.end() && ! break_condition( *iter );
++iter ) {
some_action( *iter );
}

I cannot say that I find the try-throw-catch version easier to grok or more
elegant.


I wholeheartly agree to that. I prefer to refer to the mechanism as
try-throw-catch or stack-unwinding to avoid connotations. Sometimes, when
someone says "exception are only to be thrown is exceptional
circumstances", I reply (only half-jokingly): "ok. so throw something
else".


No, but a simple for loop might be (and it also might convey intend better
than any of the alternatives).

what`s wrong with std::find_if when breaking is needed?

regards,
FM.
 
K

Kai-Uwe Bux

terminator said:
what`s wrong with std::find_if when breaking is needed?

It depends: std::find_if conveys the intend of searching. It goes against
expectations to use it with a predicate whose evaluation leaves
side-effects on the container elements. On the other hand, std::for_each
carries no such connotation (although some people feel otherwise on the
ground that the standard classifies it as non-mutating). So, std::find_if
is not a good substitute for std::for_each in the case where you just want
a loop that performs some action on an initial segment of the list.

I guess, the way to do this idiomatically in STLeese is something like:

std::for_each( begin, std::find_if( begin, end, condition ), action );


Now, I see that the OP did not specify whether there is a need for
performing an action on the elements of the sequence or whether the task at
hand is simply to find the first where a condition holds. In the later
case, std::find_if is of course perfectly fine.


Best

Kai-Uwe Bux
 
Y

yurec

/* doesn't compile :(
maybe somebody knows where is my mistake?

using namespace boost::lambda;
get_layout_func_map::const_iterator iter_func =
std::find_if(named_func_map.begin(), named_func_map.end(),

bind(&IContextToLayoutPred::Compare, ip_pred,

bind(&get_layout_func_map::value_type::second, _1)));
*/

//compiles ok!
get_layout_func_map::const_iterator iter_func =
named_func_map.begin();
const get_layout_func_map::const_iterator iter_func_end =
named_func_map.end();
while (iter_func_end != iter_func)
{
if (true == ip_pred->Compare(iter_func->second))
break;

++iter_func;
}
 
J

James Kanze

Anyone who wrote that at any place where I've ever worked would
not be allowed to touch the code again. Exceptions are NOT a
flow control mechanism.

[...]
It depends: std::find_if conveys the intent of searching. It
goes against expectations to use it with a predicate whose
evaluation leaves side-effects on the container elements.

std::find_if definitly conveys the wrong message. In addition,
the standard is very explicit: "The function object pred shall
not apply any non-constant function through the dereferenced
iterator." That definitly limits the possible uses of find_if
here.
On the other hand, std::for_each carries no such connotation
(although some people feel otherwise on the ground that the
standard classifies it as non-mutating).

On the third hand: std::for_each very definitely conveys the
intent of visiting *every* element in the sequence, exactly
once.

Perhaps the real problem is just to redefine the sequence.
Boost has some very nice iterator adaptors, for example, and if
you have an std::vector, nothing says that you can't use some
special iterators which stop along the way.
So, std::find_if is not a good substitute for std::for_each in
the case where you just want a loop that performs some action
on an initial segment of the list.
I guess, the way to do this idiomatically in STLeese is
something like:
std::for_each( begin, std::find_if( begin, end, condition ),
action );

I'm not sure about "idiomatically in STLeese". The STL is
really very poorly designed for this sort of thing. Look at the
hoops the Boost iterator adapters have to jump through.

Still, some sort of iterator adapter would seem the logical
solution.
 
S

Sohail Somani

Anyone who wrote that at any place where I've ever worked would not be
allowed to touch the code again. Exceptions are NOT a flow control
mechanism.

You should look at the Boost Graph Library's visitor mechanism then. From
what I recall, it used exceptions to stop visiting something or the other.

:)
 
K

Kira Yamato

Anyone who wrote that at any place where I've ever worked would
not be allowed to touch the code again. Exceptions are NOT a
flow control mechanism.

Any boss who think like that I would not want to work for.

Exception is certainly a flow control mechanism. Essentially it is a
glorified longjmp with smart stack unwinding that invokes destructors.
If it looks like a duck and quakes like a duck, then it is a duck.

Now of course, the question remains when is the proper use of such type
of flow control. Is it *always* only valid for error handling purposes
and nothing else? That I cannot say I have enough experience to
answer, but I find it hard to believe that there is no other uses of an
inter-stackframe branching mechanism that is aware of cleaning up
objects in those frames, beside error handling.

But I like to bring up one possible use:

In some of the algorithm books I've been reading, many of them have
terminating statements (like 'return') nested deeply in nested loops.
So, once a certain terminating condition is reached, the entire
algorithm is done and exited. If the entire algorithm has been
implemented in a single function, then the 'return' statement is
sufficient to exit the algorithm. But what if helper functions were
used and that the terminating conditions are inside these helper
functions? In this case, you can either have the helper functions
return a terminating return code or you can have the main function
setup a try-catch block and let the helper function throw an exception.
Now imagine what if helper functions can have helper functions too.
Don't you think throwing an exception here is the easiest way to return
control to the main function?

Of course, some people believe that 'return' statements should never be
in the middle of a function. A good implementation of an algorithm
would have every control flow reach the last statement before the
function-ending braces. If this is your philosophy, then to you
exception-handling should be used for error-purposes only.
 
K

Kira Yamato

Simple and elegant? in this case?

Compare

try {
std::for_each( seq.begin(), seq.end(), throwing_predicate_and_action );
}
catch ( whatever ) {}

to

for ( iterator_type iter = seq.begin();
iter != seq.end() && ! break_condition( *iter );
++iter ) {
some_action( *iter );
}

I cannot say that I find the try-throw-catch version easier to grok or more
elegant.

Hmm. The for-loop code looks more explicit than the for_each. So, why
do we bother with for_each again?

Or I should ask, why would we ever prefer a statement over a block?
 
S

Sohail Somani

Hmm. The for-loop code looks more explicit than the for_each. So, why
do we bother with for_each again?

Or I should ask, why would we ever prefer a statement over a block?

Because it is easier to understand statements than instructions. Compare:

namespace bl=boost::lambda; http://www.boost.org/doc/html/lambda.html
....
std::transform(vec1.begin(),vec1.end(),vec2.begin(),bl::_1 + 1);

To:

for(int i = 0; i < vec1.size(); ++i)
{
vec2 = vec2 + 1;
}

Over time, given enough use of the C++ standard library, you can gain an
understanding of what code is doing by casual reading without having to
scrutinize what the code is doing at the instruction level. So in the
first example, it is obvious from reading a single line of code, that
someone is intending to add 1 to each element in a vector and assign it
to another. I need to read and grok 5 lines for the second version.

At least that is my 2 minute explanation!
 
A

alan

/* doesn't compile :(
maybe somebody knows where is my mistake?

using namespace boost::lambda;
get_layout_func_map::const_iterator iter_func =
std::find_if(named_func_map.begin(), named_func_map.end(),

bind(&IContextToLayoutPred::Compare, ip_pred,

bind(&get_layout_func_map::value_type::second, _1)));
*/
Try creating a simple member function that returns the member data
'second' of a get_layout_func_map:
<your return type here> get_layout_func_map::get_second(){
return second;
}

then replace get_layout_func_map::value_type::second with
get_layout_func_map::get_second. As an aside, you might also
profitably add "inline" to the above member function, if you take care
to add the definition to the header file.

get_layout_func_map::value_type may not exist in your
get_layout_func_map class, and even if it does, it might not have a
member called second. Did you (or who created get_layout_func_map, or
one of its public base types) write such a member?

boost::bind requires the first argument to be either function or a
function object (i.e. belonging to a class that overloads the ()
operator or function call operator)
//compiles ok!
get_layout_func_map::const_iterator iter_func =
named_func_map.begin();
const get_layout_func_map::const_iterator iter_func_end =
named_func_map.end();
while (iter_func_end != iter_func)
{
if (true == ip_pred->Compare(iter_func->second))
break;

++iter_func;
}
This seems quite clear enough; using boost::bind may just make things
look more horrible.
 
A

Alf P. Steinbach

* Sohail Somani:
Hmm. The for-loop code looks more explicit than the for_each. So, why
do we bother with for_each again?

Or I should ask, why would we ever prefer a statement over a block?

Because it is easier to understand statements than instructions. Compare:

namespace bl=boost::lambda; http://www.boost.org/doc/html/lambda.html
...
std::transform(vec1.begin(),vec1.end(),vec2.begin(),bl::_1 + 1);

To:

for(int i = 0; i < vec1.size(); ++i)
{
vec2 = vec2 + 1;
}

Over time, given enough use of the C++ standard library, you can gain an
understanding of what code is doing by casual reading without having to
scrutinize what the code is doing at the instruction level. So in the
first example, it is obvious from reading a single line of code, that
someone is intending to add 1 to each element in a vector and assign it
to another. I need to read and grok 5 lines for the second version.

At least that is my 2 minute explanation!


Huh.

The second code snippet is by far most clear and at-a-glance.

However, it caused me to look twice because it's so non-idiomatic,
Pascal style that at first glance one thinks there must be some reason
for that, that it doesn't do what it seems to: in C++ we use ++.


Cheers, & hth.,

- Alf
 
K

Kai-Uwe Bux

James said:
Anyone who wrote that at any place where I've ever worked would
not be allowed to touch the code again.
Agreed.


Exceptions are NOT a flow control mechanism.

Try-throw-catch _is_ a flow control mechanism. It's a jump, something like a
goto except that you actually may not know the target of the jump since
most of the time client code provides the catch-handler. So, technically,
it's even more messy than goto. That's of course the reason that coding
standards tend to restrict the use of this flow-control mechanism.

However, which uses are and which are not acceptable has little basis in the
technical side of things; and most of those decisions are consequences of
C++ culture. Caused by precedence and guidelines pronounced by influential
people, programmers came to expect try-throw-catch to indicate error
handling and compiler writers came to heavily optimize for the no-throw
path. It's become a tradition to use try-throw-catch for failure handling
only. On a different planet, where they have a programming language with
the same syntax and semantics as C++, the cultural attitude toward
try-throw-catch could be very different.

Of course, none of that indicates that going against established culture is
a good thing. Code should be written for other humans to understand. Taking
into account their expectations is a must.


Best

Kai-Uwe Bux
 
S

Sohail Somani

std::transform(vec1.begin(),vec1.end(),vec2.begin(),bl::_1 + 1);

To:

for(int i = 0; i < vec1.size(); ++i)
{
vec2 = vec2 + 1;
}

The second code snippet is by far most clear and at-a-glance.

However, it caused me to look twice because it's so non-idiomatic,
Pascal style that at first glance one thinks there must be some reason
for that, that it doesn't do what it seems to: in C++ we use ++.

Sure! You also managed to miss the deliberate bug which was part of the
point :)
 
A

Alf P. Steinbach

* Sohail Somani:
std::transform(vec1.begin(),vec1.end(),vec2.begin(),bl::_1 + 1);

To:

for(int i = 0; i < vec1.size(); ++i)
{
vec2 = vec2 + 1;
}

The second code snippet is by far most clear and at-a-glance.

However, it caused me to look twice because it's so non-idiomatic,
Pascal style that at first glance one thinks there must be some reason
for that, that it doesn't do what it seems to: in C++ we use ++.

Sure! You also managed to miss the deliberate bug which was part of the
point :)


I presume you're referring to the bug in the template version where vec2
elements are assigned from vec1?

Yes, template metaprogramming does tend to introduce such bugs, because
so much is implicit and so much defined elsewhere.

However, the irony of your article was too well hidden for me to grok. ;-)

Cheers,

- Alf
 

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,955
Messages
2,570,117
Members
46,705
Latest member
v_darius

Latest Threads

Top