Subject: [Dev] STL for_each underwhelms me

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

At first I though the 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

---

From: Brent Verner <[email-masked]>
Date: Mon, 11 Feb 2002 13:17:03 -0500

[2002-02-11 12:57] M. Mueller (bhu5nji) said:
| At first I though the 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 

What specifically do you want for_each to do for you?

This page is a good synopsis of for_each which shows a stateful
functor.
  http://www.sgi.com/tech/stl/for_each.html

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: "M. Mueller (bhu5nji)" <[email-masked]>
Date: Mon, 11 Feb 2002 15:07:12 -0500

> What specifically do you want for_each to do for you?

I have a map of entries where the key is a transaction ID and the entries 
describe a socket connection and related query, timestamp, state, response, 
etc.  For each entry, do a non-blocking read on stream socket, check to see 
that the request is fully formed and if so, change the state to re-form the 
query and forward.  This is part of a proxy server.

I tried : for_each(xmap.begin(), xmap,end(), clientRead);

clientRead is a member of proxy server class object.

Heeding the compiler (gcc 3.0) error I changed to: 
or_each(xmap.begin(), xmap,end(), &classname::clientRead);

Still get "out of scope" error.

for_each likes functions, not methods, as far as I can tell.  I assume the 
compiler needs to be "tricked" into thinking the method is a function.

If that analysis is correct, then I have to wonder about writing "tricky" 
code.

>   http://www.sgi.com/tech/stl/for_each.html

This example seems like it uses operator overloading to make for_each do 
something more complicated that it would otherwise be capable of.  This does 
not seem like a use of operator overloading "to allow a programmer to provide 
a more conventional and convenient notation for manipulating class objects".  
(Quote from 7.1 C++ by Stroustrup.)

The example seems to be a template.  So now it has the advantage of being 
generic.  On the other hand, the number of test cases needed has just gone up 
dramatically.  

--------------------------------------------------------------------------

template<class T> struct print : public unary_function<T, void>
{
  print(ostream& out) : os(out), count(0) {}
  void operator() (T x) { os << x << ' '; ++count; }
  ostream& os;
  int count;
};

int main()
{
  int A[] = {1, 4, 2, 8, 5, 7};
  const int N = sizeof(A) / sizeof(int);

  print<int> P = for_each(A, A + N, print<int>(cout));

// I find "print<int>" and "print<int>(cout)" difficult to read - I have no
// familiarity with "Z" and "Z(func)" being meaningful is other contexts.   
// When the '+' is overloaded for strings, I find string1 + string2 easy to 
// understand.

  cout << endl << P.count << " objects printed." << endl;
}

---------------------------------------------------------------------------
An alternative that seems quite a bit easier to understand.

int main()
{
  int A[] = {1, 4, 2, 8, 5, 7};
  const int N = sizeof(A) / sizeof(int);

  int count=0;
  for (int i=0; i < N; i++) 
	{
	cout << A[i] << ' ';
	count++;
	}
  cout << endl << count << " objects printed" << endl;
}

I can't help feeling that the operator overloading used with for_each is a 
sophisticated way to get more stuff into a unary function.  I have no problem 
with for_each as long as simple unary functions are used.  Introducing 
operator overloading to squeeze more functionality into the limits of the 
unary function results in complicated code.  Maybe the complicated code is 
more versatile, but that means there are more test cases to perform.  
Operator overloading with for_each also yields terse code, which is 
esthetically pleasing.   The esthetics are lost on myself after I've gazed at 
the well-crafted code for an hour and I am still not sure of what it does.

 I'm going to keep studying this operator overloading subject.  I havn't used 
it much.

Mike M.

---

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

> Still get "out of scope" error.

You're getting an 'out of scope' error probably because clientRead
is not a static function.  Basically, for_each needs a regular function,
*or* a function object *or* a function adaptor.  What you could do
is write a function adaptor (sometimes called a functor, I believe)
that you pass your proxy object to and it acts as a shim, transfering
for_each's calls to the correct method.  For example

struct clientReadAdaptor {

  clientReadAdaptor(classtypename *classname) : myclass(classname) {}

  void operator() (xmaptypename *xmap) { myclass->clientRead(xmap); }

private:
  classtypename *myclass;
};

Then you could just do

for_each(xmap.begin(), xmap.end(), clientReadAdaptor(&classname));

> compiler needs to be "tricked" into thinking the method is a function.

Not tricked.  Told how to call it.  It can't call the method because it
doesn't know anything about the classes vtable (because it doesn't
know anything about the class).
 
> If that analysis is correct, then I have to wonder about writing "tricky" 

You just have to have the right mindset.  Oh, and understand about
the difference between calling a function and calling a method.

> generic.  On the other hand, the number of test cases needed has just gone up 

Easier to understand, maybe, but what if you have to do the same thing
over 100 times?  1000 times?  What if you don't do it the same way
each time?  How about the nubmer of test cases then?
 
> the well-crafted code for an hour and I am still not sure of what it does.
It does get easier to understand after you adjust how you think about
the code.  And really, it's just another way of doing the same thing.
Don't think of it as "sophisticated", just think of it as something
different.  C++ is inherently different than C and without realizing
this, you can't really make use of the power C++ provides you.

>  I'm going to keep studying this operator overloading subject.  I havn't used 
Perhaps it would help instead of calling it "operator overloading"
to call it a function adapter.  What do you do if you want to plug
a 3 prog electrical plug in a 2 prog socket?  You get an adapter.
That's all these object are doing.  The analogy is not perfect,
however, because going from 3 prong to 2 prong you lose something.
By using function adapters, I personally believe you gain a lot
over a simple unary function.

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:53:57 -0500

On Monday 11 February 2002 03:35 pm, you wrote:
> > describe a socket connection and related query, timestamp, state,
> > response, etc.  For each entry, do a non-blocking read on stream socket,
> > check to see that the request is fully formed and if so, change the state
> > to re-form the query and forward.  This is part of a proxy server.
> >

So that's what a functor is.  Function adapter is a better name - by far.

> for_each's calls to the correct method.  For example

Thank you.  Shim is good.

> > for_each likes functions, not methods, as far as I can tell.  I assume
> > the compiler needs to be "tricked" into thinking the method is a
> > function.
>

Function is an address and method is an offset from the base of the table?

> > something more complicated that it would otherwise be capable of.  This
> > does not seem like a use of operator overloading "to allow a programmer
> > to provide a more conventional and convenient notation for manipulating
> > class objects". (Quote from 7.1 C++ by Stroustrup.)
> >
> > generic.  On the other hand, the number of test cases needed has just
> > gone up dramatically.
> >
> > // I find "print<int>" and "print<int>(cout)" difficult to read - I have
> > no // familiarity with "Z" and "Z(func)" being meaningful is other
> > contexts. // When the '+' is overloaded for strings, I find string1 +
> > string2 easy to // understand.
> >
> > -------------------------------------------------------------------------
> >-- An alternative that seems quite a bit easier to understand.
> >
> > int main()
> > {
> > I can't help feeling that the operator overloading used with for_each is
> > a sophisticated way to get more stuff into a unary function.  I have no
> > problem with for_each as long as simple unary functions are used. 
> > Introducing operator overloading to squeeze more functionality into the
> > limits of the unary function results in complicated code.  Maybe the
> > complicated code is more versatile, but that means there are more test
> > cases to perform. Operator overloading with for_each also yields terse
> > code, which is esthetically pleasing.   The esthetics are lost on myself
> > after I've gazed at the well-crafted code for an hour and I am still not
> > sure of what it does.
>

I am truly amazed how the containers in STL have so rapidly changed that way 
I approach design.   I've OO-ing things in my product (www.signalnetware.com) 
since last summer and I've had some handsome payoffs for the work.  I can 
sense their is more value to be had.  I really got frustrated with the 
for_each algorithm, though.

>
> >  I'm going to keep studying this operator overloading subject.  I havn't
> > used it much.
>
> to call it a function adapter.  

"Function adapter" helps quite a bit.

What do you do if you want to plug

---

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

> So that's what a functor is.  Function adapter is a better name - by far.

I agree.  I think the name functor comes from something in higher
mathematics, so if you're familiar with that it works ok.  For those
of us who are mere mortals, however... :-)
 
> Function is an address and method is an offset from the base of the table?

Exactly!  To call a method you actually need two addresses: that of the
class, and that of the method inside the class (i.e. table base offset).
Functions only require one.  That's why you can get into trouble
trying to pass a non-static method.  (BTW, static methods are the same
for each class, so they're implemented exactly like a regular function.
That's why you can substitute a static method for a regular function.)
 
> for_each algorithm, though.

Well, I personally don't consider containers an example of OO.  Instead,
I consider them an example of generic programming, but hopefully the
result is the same: better code and maintainance.

Tanner