Advertisement

How to use mem_fun with for_each correctly

Started by July 12, 2001 10:51 AM
16 comments, last by Kylotan 23 years, 7 months ago
Ok, here's the way I was trying it (apologies for the sad variable names ):
for_each(badGuys.begin(), badGuys.end(), mem_fun(&App::DrawCreature));  
badGuys is a
std::vector<Creature*> 
. App::DrawCreature is defined thus:
bool DrawCreature(Creature* p);// (inside class App)  
And the unfriendly error I receive is: error C2784: 'class std::mem_fun_t<_R,_Ty> __cdecl std::mem_fun(_R (__thiscall _Ty::*)(void))' : could not deduce template argument for '<Unknown>' from 'bool (__thiscall App::*)(class Creature *)' So, what am I doing wrong? Edited by - Kylotan on July 12, 2001 6:05:24 PM
OK, I''m gonna strap on a flame-retardant suit here. That was an excellent question, and it took me about half an hour to figure it out. The reason I''m wary is that the solution is not at all fun, and I''m afraid I''ll frighten people away from STL forever. Regardless, here it is:
  #include <vector>#include <iostream>#include <algorithm>#include <functional>using namespace std;class Creature;class App{public:  bool DrawCreature(Creature* p);};bool App::DrawCreature (Creature *p){  cout << "drew creature: 0x" << p << endl;  return true;}int main (){  App a;  vector<Creature*> badGuys;  badGuys.push_back ((Creature *) 1);  badGuys.push_back ((Creature *) 2);  badGuys.push_back ((Creature *) 3);  for_each (badGuys.begin (), badGuys.end (),     bind1st (mem_fun1(&App::DrawCreature), &a));  return 0;}  


OK, that''s the answer, now let''s figure out why.

The root of the problem is that for_each calls its third parameter on each element contained (type Creature* here). The third parameter must be a free function operator () that takes a Creature * argument. The problem is that it''s not allowed to be a member function.

The solution is to create a functor class that constructs itself correctly with "a" (the app object) and calls a->DrawCreature in its operator (). You can do that by hand, or you can use the STL bindings as I''ve done.

The reason mem_fun is the wrong thing to do is that mem_fun calls a member function of Creature*, not a member function of some other class. If Creature had a function Creature::Draw (), then the for_each call you have listed would do exactly what you wanted: for each Creature *x in your vector, x->Draw () gets called. This is what mem_fun does.

Well, that''s obviously not what you want. What''s closer to you want is mem_fun1, which constructs a function mem_fun1_t which is this bad boy:
  template<class R, class T, class A>    struct mem_fun1_t : public binary_function<T *, A, R> {    explicit mem_fun1_t(R (T::*pm)(A));    R operator()(T *p, A arg);    };  

R is the return type (bool in your case).
T is the class whose member functionw we want to call: App.
A is the argument type, Creature* here.

This almost gets us what we want. We now have an operator () that takes an App* and a Creature*, and will do the function we want on each of those. But wait, for_each requires that operator () takes one parameter, not two. D''oh!!

So, we have the lovely bind1st, that creates another functor template from this template by binding the first argument to some value we set. Hence, the functor that will call a.DrawCreature on a Creature* we pass to it is:
bind1st (mem_fun1(&App::DrawCreature), &a)

By the way, as ugly as this looks, here''s the disassembly (MSVC++ 6.0 SP3):
; 29   : ; 30   : 	for_each (badGuys.begin (), badGuys.end (), ; 31   : 		bind1st (mem_fun1 (&App::DrawCreature), &a));	mov	edi, DWORD PTR _badGuys$[ebp+8]	mov	esi, DWORD PTR _badGuys$[ebp+4]$L13093:	cmp	esi, edi	je	SHORT $L13051	push	DWORD PTR [esi]	lea	ecx, DWORD PTR _a$[ebp]	call	?DrawCreature@App@@QAE_NPAVCreature@@@Z	; App::DrawCreature	add	esi, 4	jmp	SHORT $L13093$L13051: 
Advertisement
Well. I must admit that I still don''t entirely understand. From the documentation I read, it implied that mem_fun would be able to convert a member function into a function object as I required. However, I understand and appreciate your basic point which underlies the fact that, to execute an ''App'' member function, it somehow needs to know which instance of App it applies to. And your suggested fix was correct and worked just fine. (In my code, where you used &a, I was just able to use ''this''.)

I also found the assembly listing interesting. Unless my assembly skills have left me, it seems to have optimised down to little more than a while loop with a function call inside it. Which of course, is what you would expect. But it just goes to show that the STL doesn''t produce as inefficient code as you might think by looking at all the template definitions.
I had this problem before and it''s just a lot easier to use a for() loop and iterators to iterate the vector. Stoffel is STL master but for me I have ways to go Some stl functions are lot easier to figure out with non-member functions, I find.

I rerun the vector test and stl is 3x faster. Maybe I don''t have my vector optimized but I feel I would be close or even anyways. The time saving of using stl over hand rolled containers is great. I tried the STLPort-4 and it''s 3x faster than dinkum''s stl on vector push_back() at least. I just don''t know how to build the SGI streams version of stl. His readme are confusing and I am not nmake expert either. The SGI stl has syntax errors so I can''t use it because I don''t know how to get rid of them. So I''m back to dinkum''s vc++6 stl.

What stl stoffel, kylotan you''re using?
K: the key is that mem_fun makes a functor for a member function of Creature, not of App.

JD: I showed this to my work-mate because I thought it was cool--I didn''t know how to do this till Kylotan asked the question. Anyway, he rolled his eyes & said "How much better is that than just doing the for loops that we do all over the place."

Well, I tried it and here''s what I got:
  for ( vector<Creature*>::iterator it = badGuys.begin ()    ; it != badGuys.end ()    ; ++it){  a.DrawCreature (*it);}  


This loop compiles to have 1 extra instruction when compared with the for_each solution. I highly suspect that it''s because badGuys.end () might be evaluated each time; maybe if you used a temporary const_iterator = badGuys.end () at the beginning and then made it compare against that they would compile to the same thing. But now you''re talking about 10 lines as opposed to 2--does this help complexity? And doesn''t this just prove the point that you should use the built-in algorithms because it''s easy to make efficiency mistakes?

I should say that I do not practice what I preach, and have yet to start using these algorithms everywhere; but I''m slowly convincing myself that I should.

PS: I''m using the Microsoft version of STL that ships with VC 6.0, SP3. It''s important to keep using that because I work in a large company and our libraries get around to lots of people--we don''t want to have to distribute standard libs or have to support other people having to instal them simply to use my bitchin'' SoundOutQueue class.
From the MSDN STL chapter about mem_fun

  template <class R, class T>    struct mem_fun_t : public unary_function<T *, R> {    explicit mem_fun_t(R (T::*pm)());    R operator()(T *p);    };  


The template class stores a copy of pm, which must be a pointer to a member function of class T, in a private member object. It defines its member function operator() as returning (p->*pm)().

From the last linem, the App::DrawCreature should be defined as

Add::DrawCreature();

Any parameters would require function adapters.


Edited by - Void on July 12, 2001 10:28:45 PM
Advertisement
Stoffel wrote:

quote:

I should say that I do not practice what I preach, and have yet to start using these algorithms everywhere; but I''m slowly convincing myself that I should.



This is an excellent observation and one that I keep asking myself all the time. I feel in order to write efficient code you must dig deep into the STL source and get your hands dirty to find out what''s going on. The second option is to ignore the detective work and write not optimal code. Looks like STL is loosing it''s plain simpleness when you have to use exotic methods that you''ll forget the moment you write them, among tons of physics, ai, sound, csg, collision detection, scene graph code. I feel like I''m neglecting all those other things that make up the game in exchange for really understanding STL. Do we as coders have to specialize further and not be able to pull the various parts together into a whole because each part is infinitely complex?

I had the same thing happening with MFC. As soon as I tried to do something outside the mfc jurisdiction I had to plow through its source code for days until I found a solution. I should have spent the time instead on elements of game that could bring it close to completion. So I stuck with win32 because I have one doc to go to and the api is bare as it can get. I need lego blocks not half finished castle that I then have to study from foundation up so that I could extend it. This is why C is such a cool language because it has few blocks yet you can create a lot. C++ has more blocks that take time to examine and use. Thanks for listening.

Ignoring the complexity of the full STL method, there''s another glaring problem with it. AFAICT, You have to create a new functor for each & every method you want to use with for_each. That is a *HUGE* pain-the-ass, maximumal inconvenience. If I am wrong, please let me know...
I think this is a limitation of C++; it has to do with the lack of ''first class functions''. I don''t think you would have this problem in smalltalk.

This is a macro I made for an observer template:
  #define FOR_EACH_OBSERVER_CALL(mthd) \	{\	tyListObserver::iterator it     = m_listObserver.begin();\	tyListObserver::iterator itStop = m_listObserver.end();\	for(;it!=itStop; it++)\		{\		(*it)->mthd;\		}\	}//maybe the pre-processor isn''t evil afterall...  


An example of the macro in use, invoking an OnConnect Event in a CNetworkClient class:
  HRESULT CNetworkClientDx8::ConnectComplete(DWORD dwMessageId, PVOID pMsgBuffer)	{	PDPNMSG_CONNECT_COMPLETE msg = (PDPNMSG_CONNECT_COMPLETE)pMsgBuffer;	m_dpnhConnect = 0;		HRESULT hr;	if(FAILED(hr=msg->hResultCode))		FOR_EACH_OBSERVER_CALL(OnError(this, hr))	else		FOR_EACH_OBSERVER_CALL(OnConnect(this))		return(hr);	}  
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
quote:
Original post by Kylotan
Well. I must admit that I still don't entirely understand. From the documentation I read, it implied that mem_fun would be able to convert a member function into a function object as I required. However, I understand and appreciate your basic point which underlies the fact that, to execute an 'App' member function, it somehow needs to know which instance of App it applies to. And your suggested fix was correct and worked just fine. (In my code, where you used &a, I was just able to use 'this'.)

I also found the assembly listing interesting. Unless my assembly skills have left me, it seems to have optimised down to little more than a while loop with a function call inside it. Which of course, is what you would expect. But it just goes to show that the STL doesn't produce as inefficient code as you might think by looking at all the template definitions.



mem_fun is suppose to compile to the correct version, mem_fun1, but this only works if the compiler supports Partial template specialization. which MSVC++ compiler DOES NOT. Hell, you can't even use a const function as a param to mem_fun because of that and you also cannot use a void function

Borland has a much better compile than MSVC++. It is really close the ansi standard. You can take textbook examples and they will work 98% of the time. You can say the same thing of GCC and the derivative.

Magmai : You are right, it can be a pain in the ass to create a functor, but with a functor performance is much higher. Because the compiler can inline everything. if you would use a loop or a function pointer, the compiler will probably not be able to inline. So that is a big +. Actually, the C++ sort on vector can be faster from 20 to 160% than the C qsort because qsort uses a function pointer(no inlining), but C++ sort uses a functor and gets inlined. Other functions may have the same effect.

Also, the implementers of the stl are free to speciliaze the algorithm to make them work as fast as they can for a type of container.

Algorithm are a real speed boost for C++. And please, do NOT use MSVC++ to test it, because it does not support partial template specilazition and member template specialization fails somethimes so they cannot write specialize version of algorithm. It has a very crappy support for templates. It is not a good compiler to do pure C++ in. (by pure, I mean using classes and also most of stl when you can use it). See above for compilers that can do a better job.

Also, the written code is smaller. I prefer a for_each with 3 arguments then while loop. The for_each is much more easier to understand. Well somethimes it gets very ugly if you start to use a bunch of predicate in it, but someone clever can break it up in swallowable pieces.



Edited by - Gorg on July 13, 2001 9:49:34 AM

Edited by - Gorg on July 13, 2001 11:10:21 AM
quote:
Original post by Gorg
mem_fun is suppose to compile to the correct version, mem_fun1, but this only works if the compiler supports Partial template specialization.


This is incorrect. mem_fun''s constructor tells all:
  template<class R, class T>    mem_fun_t<R, T> mem_fun(R (T::*pm)());  

mem_fun takes an argument of type R (T::*pm)(). It has a return type, and it''s a member of a class, but it can have no arguments . It returns a functor whose operator () takes a T parameter. There is only one class involved, but the poster wanted two classes.

Again, mem_fun is meant to be used when you have a container of X and you want to call X.f (). This is not what is happening. We have a container of X, and we want to call Z::g (X x). Completely different things. mem_fun and mem_fun1 have nothing to do with each other--they each create completely different functor objects. If you are able to get g++ to compile the for_each using mem_fun (&App::DrawCreature), please let me know what version you''re using, as mine replies:
zinc{53}> g++ test.cpp/pkg/gnu/gcc-2.95.2/lib/gcc-lib/sparc-sun-solaris2.8/2.95.2/../../../../includeg++-3/stl_algo.h: In function `class mem_fun1_t for_eacheature **, mem_fun1_t >(Creature **, Creature **, mem_fun1t)'':test.cpp:38:   instantiated from here/pkg/gnu/gcc-2.95.2/lib/gcc-lib/sparc-sun-solaris2.8/2.95.2/../../../../includeg++-3/stl_algo.h:83: no match for call to `(mem_fun1_t) (Ceature *&)''/pkg/gnu/gcc-2.95.2/lib/gcc-lib/sparc-sun-solaris2.8/2.95.2/../../../../includeg++-3/stl_function.h:527: candidates are: bool mem_fun1_t:operator ()(App *, Creature *) constzinc{54}> 

It tries to get to mem_fun1, but there''s no way to tell it _which_ App is the one I want, just that I want something that takes an App. That''s why you need to use bind1st.
quote:

Hell, you can''t even use a const function as a param to mem_fun because of that and you also cannot use a void function


This is unfortunately true.

This topic is closed to new replies.

Advertisement