Subject: [Dev] STL for_each underwhelms me

From: "M. Mueller (bhu5nji)" <[email-masked]>
Date: Mon, 11 Feb 2002 13:13:51 -0500

(darn laptop and fat fingers...)

At first I though the STL "for_each" algorithm was going to be handy.  Then I 
tried using it.  

I know very well what a "unary function" is and how they are different from 
"binary functions" and "unary methods".  I searched the web for advice on how 
to get around the limitations of for_each only operating in unary functions.  
The workarounds were studies in obuscation.   Or maybe I should look deeper?  
Am I missing the finer points?  Here's how I understand things right now.

Why is the following code structure in need of replacement by such a limited 
algorithm as for_each?

The mortal way:

map<int,int> xmap;

// load map...

map<int, int>::iterator i;
for(i = xmap.begin(), i != xmap.end(), i++)
{
	// do any darn thing you want to
	// in plain old un-elegant but readable
	// code
}

The for_each way:

Either:
for_each(xmap.begin(), xmap.end(), unary_function_only);
Or:
for_each(xmap.begin(), xmap.end(), class_with_overloaded_operators);
Or:
for_each(xmap.begin(), xmap.end(), 
magically_transformed_things_that_appear_to_be_UF);

The first way is OK, but it's limited to only unary functions.  I tried to 
use a unary method and kept getting "out of scope" errors during compile.  
The tranformation techniques I saw on the web were so obtuse that they 
probably would not have survived code reviews in places I have worked in the 
past.

Someone, please turn on the light for me.

Thanks,
Mike M.

---

From: Tanner Lovelace <[email-masked]>
Date: 11 Feb 2002 15:15:51 -0500

> Someone, please turn on the light for me.

Perhaps it would help if you would explain a bit more about
what you're trying to do, and why a unary function won't work
for you?  Without knowing that, however, you probably want to
look around for documentation about a "function object" which
is an object designed to look like a function but that can
hold state. 

Say, for example, you want to take a vector of ints and calculate
it's mean value.  With a plain old for, you could loop through
each element of the vector and calculate the sum and finally 
divide by the number of elements.  With for_each, however, you
could do it like this:

// function object to process the mean value
class MeanValue {
 private:
    long num; // number of elements
    long sum; // sum of all elements
 public:
    // Constructor
    MeanValue() : num(0), sum(0) {}

    // function call
    void operator() (int elem) {
      ++num; // increment count
      sum += elem;
    }

    // return mean value
    double value () {
      return static_cast<double>(sum) / static_cast<double>(num);
    }
};

int main(void)
{
  vector<int> coll;

  // insert elements from 1 to 8
  for (int i = 1; i <=8; ++i) {
    coll.push_back(i);
  }

  // Process and print mean value
  MeanValue mv = for_each(col.begin(), coll.end(), MeanValue());
  
  cout << "mean value: " << mv.value() << endl;

  return 0;
}

Note that this also shows another feature of for_each, it 
returns the (modified) function object passed to it.  As far
as I know, this is the only STL algorithm that does that.
I believe this is an example of what you called
"class_with_overloaded_operators".

Your "magically_transformed_things_that_appear_to_be_UF" are better
know as "function adapters".  basically, they're functions that
you can pass a second (or first) argument to them and they'll
handle the fact that for_each only expects a unary function.
So, if you wanted to multiply each element by a number, you'd
need a function adapter that took the number you wanted to
multiply by and provided a function for for_each to call.

Also, note that if you're trying to modify a bunch of elements,
transform is often a better function to use.  The difference
between tranform and for_each is that for_each expects a
unary function that returns void, so if you want to modify
the elements, you need to pass them by reference.  Transform,
on the other hand, takes a unary operation that returns 
a value.  This value replaces the previous value in that
position of the container.  So, if you wanted to square a
bunch of elements, you could do either this:

void square(int &elem) { elem = elem * elem; }
for_each(coll.begin(), coll.end() square);

or this:

int square(int elem) { return elem * elem; }
transform(coll.begin(), coll.end(), // source range
          coll.begin(),             // destination range
          square);                  // operation.

But, once again, because C++ offers lots of ways to do things,
it would help if we knew just what it was you're doing. :-)

Tanner
-- 
Tanner Lovelace | [email-masked] | http://wtl.wayfarer.org/
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
GPG Fingerprint = A66C 8660 924F 5F8C 71DA  BDD0 CE09 4F8E DE76 39D4
GPG Key can be found at http://wtl.wayfarer.org/lovelace.gpg.asc
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
 Those who are willing to sacrifice essential liberties for a little 
 order, will lose both and deserve neither.  --  Benjamin Franklin 

 History teaches that grave threats to liberty often come in times
 of urgency, when constitutional rights seem too extravagant to 
 endure.  --  Justice Thurgood Marshall, 1989

---

From: "M. Mueller (bhu5nji)" <[email-masked]>
Date: Mon, 11 Feb 2002 16:07:02 -0500

> is an object designed to look like a function but that can

Nice description.  That's one light.  The David Musser STL Tutorial only 
talks about functions, never about function objects that have state.  So, now 
I can envision that the function object is "sent in" to the for_each 
algorithm.   

> multiply by and provided a function for for_each to call.

This is where I cannot clearly see the benefits of using for_each.  I am 
havine the hardest time learning why I would choose to overload operators to 
create a way to squeeze in two parms where the design called for one.  I keep 
hearing the cries of those following my code, "what the heck is going on 
here?".  Why not choose (what I think is) the simpler way - write a function 
or method and call it in a for loop?  Perhaps this is just a more explicit 
way.  It is not obvious to me that using the for_each will produce better 
performance that using the for-loop.

I am saying that STL does not create simpler code.  In general, I am very 
pleased with STL.  I practically think in sets and maps these days.  I'll 
never write another linked-list.

I suspect one of my problems with for_each is a poor understanding of 
operator overloading, especially for the "function call"

>           coll.begin(),             // destination range

The code here is quite readable.  

> it would help if we knew just what it was you're doing. :-)

See my reply to Brent Verner.

---

From: Tanner Lovelace <[email-masked]>
Date: 11 Feb 2002 16:47:23 -0500

> I can envision that the function object is "sent in" to the for_each 

Hmm... It doesn't talk about function object?  What a major
omission.  I assume you already have an STL book, since you
mention the Musser (& Sanni) one, but I'll just plug my 
personal favorite.. :-)  I looked at a lot of STL books
(and granted, the 2nd edition of Musser et all wasn't
out when I did, but..) and the one I liked the best, 
for writing style, information, understanding and examples
was, by far, Nicolai M Josuttis's *The C++ Standard Library,
A Tutorial and Reference*.  That's where I got the example
I showed you.  It talks a lot about function objects...

> performance that using the for-loop.

Well, here, the obvious answer is code reuse.  I gave a generic
function object that would calculate the mean value.  If you
compute the mean value in only one place then a for loop is
probably ok.  If you do it in more than one place, then abstracting
the code out so that you only have to write it once makes more
sense.

> operator overloading, especially for the "function call"

That might be.  Try looking up functor.  The Design Pattern book gives
this reference for a use of the operator() in making a functor:

James O. Coplien, *Advanced C++ Programming Styles and Idioms*.  
Addison-Wesley, Reading, MA, 1992.

I'm sure there are others...
 
Tanner

---

From: "M. Mueller (bhu5nji)" <[email-masked]>
Date: Mon, 11 Feb 2002 19:42:35 -0500

> I showed you.  It talks a lot about function objects...

I'm going to take another look at the Josutti book.  i got the examples 
package from the web.  

I am finding the Musser book is probably better read front to back.  I do not 
often read a book in that fashion.

---

From: Brent Verner <[email-masked]>
Date: Mon, 11 Feb 2002 20:36:24 -0500

[2002-02-11 19:42] M. Mueller (bhu5nji) said:
| 
| I'm going to take another look at the Josutti book.  i got the examples 
| package from the web.  

Beware that the Josuttis book contains some examples (especially
WRT iostreams, IIRC) that will not work with g++-3 and libstdc++-v3

Also beware of examples in Stroustrup's book as well.

AFAIK, the only (correct) publication regarding the newest c++
standard is that standard itself (ISO/IEC 14882:1998)

| I am finding the Musser book is probably better read front to back.  I do not 
| often read a book in that fashion.

If you haven't yet got Austern's "Generic Programming and the STL",
I highly recommend it for a wonderful look into the concepts behind
the design of the STL.

cheers.
  brent

-- 
"Develop your talent, man, and leave the world something. Records are 
really gifts from people. To think that an artist would love you enough
to share his music with anyone is a beautiful thing."  -- Duane Allman

---

From: Tanner Lovelace <[email-masked]>
Date: 11 Feb 2002 22:14:22 -0500

> WRT iostreams, IIRC) that will not work with g++-3 and libstdc++-v3

Really?  Can you tell me which ones?  I'd be interested in
knowing which ones and why.

> standard is that standard itself (ISO/IEC 14882:1998)

Well, that's not too surprising.  Until hte standard was
ratified, it was moving pretty fast.
 
> I highly recommend it for a wonderful look into the concepts behind
I have the Austern book and Matt does know what he's talking
about, but I find it very difficult to read.  I'm much
happier with the Josuttis book.  Just my personal opinion.

Tanner