[Dev] STL for_each underwhelms me

M. Mueller (bhu5nji) dev@trilug.org
Mon, 11 Feb 2002 16:53:57 -0500


On Monday 11 February 2002 03:35 pm, you wrote:
> On Mon, 2002-02-11 at 15:07, M. Mueller (bhu5nji) wrote:
> > > 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.
>
> 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)

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


> that you pass your proxy object to and it acts as a shim, transfering
> for_each's calls to the correct method.  For example

Thank you.  Shim is good.

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

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

>
> > If that analysis is correct, then I have to wonder about writing "tricky"
> > code.
>
> You just have to have the right mindset.  Oh, and understand about
> the difference between calling a function and calling a method.
>
> > 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;
> > }
>
> 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?
>
> > 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.
>
> 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 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.
>
> Perhaps it would help instead of calling it "operator overloading"
> to call it a function adapter.  

"Function adapter" helps quite a bit.

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