std::for_each + break

S

Sohail Somani

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.
;-)

No, that wasn't it but I suppose the example wasn't full enough. I'll let
you off this time.

Your attitude towards templates is telling though.
 
A

Alf P. Steinbach

* Sohail Somani:
No, that wasn't it

It wasn't?

Then I guess that in your view the first snippet defined the wished for
effect, and the second had a "bug" in the sense that it didn't do the same?

If so, then you're suffering from the "others should read my mind" syndrome.

but I suppose the example wasn't full enough. I'll let
you off this time.

Your attitude towards templates is telling though.

You will never convince anyone of adopting your point of view by saying
they have "attitude" (perhaps that counts as "attitude" with you?).

Any "attitude" you're imagining is wholly in your imagination.

Please turn down the reliance on whatever it is that comes across in
your articles as if you're imagining we're all telepathic: that one
should read your mind, and that you're able to read e.g. mine.


Grumble,

- Alf
 
S

Sohail Somani

* Sohail Somani:
Then I guess that in your view the first snippet defined the wished for
effect, and the second had a "bug" in the sense that it didn't do the
same?

If so, then you're suffering from the "others should read my mind"
syndrome.

Dude chill. If you read the rest of the email, I did concede that! In
fact, its the next line you quote:
but I suppose the example wasn't full enough. I'll let you off this
time.
[snip]
Grumble,

And how!
 
K

Kai-Uwe Bux

Alf said:
* Sohail Somani:

It wasn't?

Then I guess that in your view the first snippet defined the wished for
effect, and the second had a "bug" in the sense that it didn't do the
same?

If so, then you're suffering from the "others should read my mind"
syndrome.



You will never convince anyone of adopting your point of view by saying
they have "attitude" (perhaps that counts as "attitude" with you?).

Any "attitude" you're imagining is wholly in your imagination.

The following line is not in the poster's imagination but in your post:

Since the OP used boost::lambda, the operation in the template was defined
in-place and not elsewhere. In this line, you are clearly talking about
template metaprogramming in general and not just about the poster's
example.

Although the issues you mention are real (especially the push to use
functors defined elsewhere), I do not know of any study that would
demonstrate that this makes template based programs more prone to bugs such
as the one you found (pretending it is one) than their non-template counter
parts. If you have data to support your contention, I would be very
interested.


Best

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
The following line is not in the poster's imagination but in your post:

That's a fact. I fail to see how a fact can be an attitude. If you
want examples just check recent threads in this NG: in many cases,
nobody know quite why something works or why something doesn't work. It
reduces programming to try-and-fail experimention. And as those experts
and authorities who have addressed this issue recommend, the only sane
way to proceed is to either don't use it or else to employ at least two
different compilers and keeping always updated with latest versions.

Since the OP used boost::lambda, the operation in the template was defined
in-place and not elsewhere. In this line, you are clearly talking about
template metaprogramming in general and not just about the poster's
example.

Also about the example. One who is not familiar with the templated
functionality used will have to look up the definitions (they're
elsewhere). Not to mention that it's an unreadably long utterance: it
doesn't matter that in total it's shorter than the loop.

And yes, I know how to redesign so that it will be readable, avoiding
all the verbosity with begin and end and so on.

But readability then comes at the cost of even further indirection and
non-standard functionality that's harder to find documentation of.

Although the issues you mention are real (especially the push to use
functors defined elsewhere), I do not know of any study that would
demonstrate that this makes template based programs more prone to bugs such
as the one you found (pretending it is one) than their non-template counter
parts. If you have data to support your contention, I would be very
interested.

I think Verity says it better than any statistics: <url:
http://www.regdeveloper.co.uk/2006/08/08/cplusplus_loops/>.



Cheers, & hth.,

- Alf
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux:

That's a fact.

So far, I only see a claim :)

For the record: I strongly agree with the sentiment that generic algorithms
that require functors leave much room for improvement on readability. We
had a recent thread here:

http://groups.google.com/group/comp.lang.c++/browse_frm/thread/47f5e807f65896e2/

where the alleged single-line solution in STLeese was very much buggy and
the corrected version was very much non-readable.

However, as a purely empirical matter, I am neither convinced that the use
of STL algorithms "tends to introduce" the kind of bug pointed to above
nor, say, that STL loops tend to be more buggy than their hand-coded
counter parts.

On a more general note: The anti-correlation of buggyness and readability is
a complex matter. I know pieces of code that are bug-free _because_ they
are highly non-readable. This may happen in math-programming for instance
when you follow a published algorithm like LLL-reduction and implement it
so that code resembles the version form the original paper as closely as
possible; very often you will end up using strange variable names
and "goto" all over the place. The advantage is that you have an algorithm
that is proven correct.

I fail to see how a fact can be an attitude.

Sometimes, pointing out certain facts can be indicative of an attitude.

_However_, neither do I claim nor do I want to imply that (a) your statement
is indicative of an attitude of yours, or (b) that you have pointed out a
fact.

If you
want examples just check recent threads in this NG: in many cases,
nobody know quite why something works or why something doesn't work. It
reduces programming to try-and-fail experimention. And as those experts
and authorities who have addressed this issue recommend, the only sane
way to proceed is to either don't use it or else to employ at least two
different compilers and keeping always updated with latest versions.

Could you please be more specific and provide some pointers. There are
many "recent threads" in this NG and I would appreciate if you could point
toward those that you think provide data points your claim.

[snip]
I think Verity says it better than any statistics: <url:
http://www.regdeveloper.co.uk/2006/08/08/cplusplus_loops/>.

Thanks a lot! I enjoyed reading that (especially since I sympathize with the
sentiment).

However, it does not actually present any data in support of the claim that
generic algorithms tend to introduce the kind of bug you pointed out.


Best

Kai-Uwe Bux
 
S

Sohail Somani

However, it does not actually present any data in support of the claim
that generic algorithms tend to introduce the kind of bug you pointed
out.

I'm done with this thread, but I just want to point out that there was no
bug. That is an intended mode of use for std::transform: Perform an
operation on each element in an array and assign it to another.
 
S

Sohail Somani

I'm done with this thread, but I just want to point out that there was
no bug. That is an intended mode of use for std::transform: Perform an
operation on each element in an array and assign it to another.

Damnit, here you go.

// file.cpp
#include <iostream>
#include <vector>
#include <boost/lambda/lambda.hpp>
#include <algorithm>

namespace bl = boost::lambda;

int main()
{
std::vector<int> v1(10,5);
std::vector<int> v2(10);
// v2 = v1 + 1 for i in [0,v2.size())
std::transform(v1.begin(),v1.end(),v2.begin(),bl::_1 + 1);
std::for_each(v1.begin(),v1.end(),std::cout << bl::_1 << '\n');
std::for_each(v2.begin(),v2.end(),std::cout << bl::_1 << '\n');
}

Output:
5
5
5
5
5
5
5
5
5
5
6
6
6
6
6
6
6
6
6
6
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
Thanks a lot! I enjoyed reading that (especially since I sympathize with the
sentiment).

However, it does not actually present any data in support of the claim that
generic algorithms tend to introduce the kind of bug you pointed out.

Glad you enjoyed it.

However, I refuse to do research to cough up facts and stats and thread
references and whatever other evidence, which then will be disputed,
sort of like global warming. Anyway, it would be very post-hoc. And
even if you currently really disagree, I know you'll come around... ;-)

I think the selection of a proper level of abstraction is much like
taxation: 0% tax => no revenue (here we're at the assembly and goto
spaghetti level of coding), 100% tax => no revenue (here we're at the
top of C++ template meta-programming), somewhere in the middle => Martin
Gardner's techno-snarl[1], multiple possible values, in some range.

Cheers,

- Alf


[1] <url: http://en.wikipedia.org/wiki/Image:Neo-Laffer-Curve.svg>
 
T

terminator

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.

that is double looping :too much run time.
despite irreadability 'find_if' gives a breakable loop that relies on
the return value of pred.This is far faster than breaking throughout
exceptions.Is there a third solution which is both fast and readable?

regards,
FM.
 
K

Kai-Uwe Bux

terminator said:
that is double looping :too much run time.

Before you come to that conclusion, you might want to measure. Note that
during the first loop, only the condition is evaluated and during the
second loop only the action is evaluated. That means, as long as
incrementing iterators is cheap compared to evaluating the condition and
performing the action, the double looping is should not be expected to be
significantly more expensive.

Moreover, optimization in compilers has come a long way and I wouldn't be
surprised if the compiler would actually fold the two loops into one
(especially since the first is only used to determine the upper bound for
the second).

But the main mantra with respect to performance is: measure, measure,
measure, measure, ...

despite irreadability 'find_if' gives a breakable loop that relies on
the return value of pred.This is far faster than breaking throughout
exceptions.Is there a third solution which is both fast and readable?

Upthread, I suggested:

for ( ... ) {
...
}

and I still find it readable and fast.


Best

Kai-Uwe Bux
 
T

terminator

Before you come to that conclusion, you might want to measure. Note that
during the first loop, only the condition is evaluated and during the
second loop only the action is evaluated. That means, as long as
incrementing iterators is cheap compared to evaluating the condition and
performing the action, the double looping is should not be expected to be
significantly more expensive.

Moreover, optimization in compilers has come a long way and I wouldn't be
surprised if the compiler would actually fold the two loops into one
(especially since the first is only used to determine the upper bound for
the second).
you have solved the case that checking precedes modification;

for(...){
if(...) break;
...
};


so what would you do if some part of modifiction is intended precede
the 'break' .I mean:

for(...){
...
if(...) break;
...
}

the elements after the breaking iterator are supposed to remain
unprocessed of course.
what would you do with multiple breaking?(like this:)

for(...){
...
if(...) break;
...
if(...) break;
...
}
But the main mantra with respect to performance is: measure, measure,
measure, measure, ...


Upthread, I suggested:

for ( ... ) {
...
}

and I still find it readable and fast.

you migth find this readable too:

for (hell_template <with_lots_of_args>::iterator
it=container.begin();...)...;

of course 'auto' can fix this in next generation of c++ compilers:

for(auto it=container.begin();...)...;

regards,
FM.
 
J

James Kanze

terminator wrote: [...]
that is double looping :too much run time.
Before you come to that conclusion, you might want to measure.
Note that during the first loop, only the condition is
evaluated and during the second loop only the action is
evaluated. That means, as long as incrementing iterators is
cheap compared to evaluating the condition and performing the
action, the double looping is should not be expected to be
significantly more expensive.
Moreover, optimization in compilers has come a long way and I
wouldn't be surprised if the compiler would actually fold the
two loops into one (especially since the first is only used to
determine the upper bound for the second).

More likely it won't. You have two loops, each of which fits
into a cache line. Replacing it with a single, larger loop
which no longer fits into the cache line is *not* an
optimization.

Of course, the compiler knows about things like cache line
width, and will do the optimization if it improves things, and
not do it if it would slow them down.

[...]
Upthread, I suggested:
for ( ... ) {
...
}
and I still find it readable and fast.

And a really good compiler might even break it into two loops,
if that would speed things up:).
 
T

terminator

terminator wrote: [...]
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.
that is double looping :too much run time.
Before you come to that conclusion, you might want to measure.
Note that during the first loop, only the condition is
evaluated and during the second loop only the action is
evaluated. That means, as long as incrementing iterators is
cheap compared to evaluating the condition and performing the
action, the double looping is should not be expected to be
significantly more expensive.
Moreover, optimization in compilers has come a long way and I
wouldn't be surprised if the compiler would actually fold the
two loops into one (especially since the first is only used to
determine the upper bound for the second).

More likely it won't. You have two loops, each of which fits
into a cache line. Replacing it with a single, larger loop
which no longer fits into the cache line is *not* an
optimization.

Of course, the compiler knows about things like cache line
width, and will do the optimization if it improves things, and
not do it if it would slow them down.

[...]
Upthread, I suggested:
for ( ... ) {
...
}
and I still find it readable and fast.

And a really good compiler might even break it into two loops,
if that would speed things up:).

why should all the ways end to Roma?
why should we leave everything to destiny?
why should everything be left to compiler`s abilitiy to auto-optimize?
 
J

James Kanze

On Nov 23, 5:18 pm, James Kanze <[email protected]> wrote:

[...]
why should everything be left to compiler`s abilitiy to
auto-optimize?

Because they can do it so much better than a human.

Of course, they do sometimes fail; when the profiler says we
must, we optimize by hand. But historically, programmers have
been notoriously bad about optimizing without using the
profiler.
 
T

terminator

[...]

why should everything be left to compiler`s abilitiy to
auto-optimize?

Because they can do it so much better than a human.

Of course, they do sometimes fail; when the profiler says we
must, we optimize by hand. But historically, programmers have
been notoriously bad about optimizing without using the
profiler.

Got it ,that junk profiler is not designed by a dumb human.
 
J

James Kanze

On Nov 23, 5:18 pm, James Kanze <[email protected]> wrote:
[...]
why should everything be left to compiler`s abilitiy to
auto-optimize?
Because they can do it so much better than a human.
Of course, they do sometimes fail; when the profiler says we
must, we optimize by hand. But historically, programmers have
been notoriously bad about optimizing without using the
profiler.
Got it ,that junk profiler is not designed by a dumb human.

No you haven't gotten it. A human using a meter measuring stick
is a lot better at determining lengths that a human just
estimating. A profiler is a measuring instrument.
 
T

terminator

[...]
why should everything be left to compiler`s abilitiy to
auto-optimize?
Because they can do it so much better than a human.
Of course, they do sometimes fail; when the profiler says we
must, we optimize by hand. But historically, programmers have
been notoriously bad about optimizing without using the
profiler.
Got it ,that junk profiler is not designed by a dumb human.

No you haven't gotten it. A human using a meter measuring stick
is a lot better at determining lengths that a human just
estimating. A profiler is a measuring instrument.

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

the question is whether to update the library or hope that the
compiler core is updated . I can not trust what I can not control.

regards,
FM.
 
K

Kai-Uwe Bux

terminator said:
[...]
why should everything be left to compiler`s abilitiy to
auto-optimize?
Because they can do it so much better than a human.
Of course, they do sometimes fail; when the profiler says we
must, we optimize by hand. But historically, programmers have
been notoriously bad about optimizing without using the
profiler.
Got it ,that junk profiler is not designed by a dumb human.

No you haven't gotten it. A human using a meter measuring stick
is a lot better at determining lengths that a human just
estimating. A profiler is a measuring instrument.
[snip]
the question is whether to update the library or hope that the
compiler core is updated . I can not trust what I can not control.

Then I take it that you implemented your own tool chain?

Chess programs regularly outplay their programmers. In the same way,
compilers regularly generate code that is better adapted to the pipeline in
the processor than a human programmer would do. However, of course, you are
free to try. You would need to use assembler, though: as long as you use a
high level language, you won't have enough control over details. E.g., the
compiler will decide for you what to put into registers at which point.


Best

Kai-Uwe Bux
 

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

Forum statistics

Threads
474,189
Messages
2,571,016
Members
47,616
Latest member
gijoji4272

Latest Threads

Top