Archived

This topic is now archived and is closed to further replies.

Seriema

std: want to use for_each !..

Recommended Posts

Seriema    634
Hi! I''m looking at my code and thinking, "what the?.. this would be so much clearer with for_each()!"
  
  ORC::ParticleFX::Container::const_iterator 
    i = particleFX.GetSystems().begin();
  for(; i != particleFX.GetSystems().end(); ++i)
    lbSystems.AddString( (*i)->GetName().c_str() );
  
but... neither mem_fun nor ptr_fun feels like the thing I need? "No lies of sugar can sweeten the sournes of reality" }+TITANIUM+{ A.K.A. DXnewbie[onMIRC]

Share this post


Link to post
Share on other sites
Sneftel    1788
mem_fun and ptr_fun are often the wrong way to go about using algorithms such as for_each. What you really should do is write a functor. A functor is a class whose instances can behave like functions; that is, they overload the parenthesis operator. Read the STL docs for more info.


How appropriate. You fight like a cow.

Share this post


Link to post
Share on other sites
Shannon Barber    1681
This is a rough one:


  
#include <string>

#include <vector>

#include <algorithm>

#include <functional>

using namespace std;

#include <boost\compose.hpp>

using namespace boost;



vector<Particle*> particles;
CListBox lbSystem;


for_each
(
particles.begin(),
particles.end(),
compose_f_gx(
bind1st(
mem_fun(CListBox::AddString),
&lbSystem
),
compose_f_gx(
mem_fun_ref(std::string::c_str),
mem_fun(Particle::GetName)
)
)
);


You can see why writing a functor is often easier.

Share this post


Link to post
Share on other sites
petewood    819

    

struct addStringToListBox {
addStringToListBox(CListBox& list)//create object that remembers the listbox

: list_(list) {}
void operator()(const Particle* particle) {// function call to add name to listbox

list_.AddString(particle->GetName().c_str());
}
private:
CListBox& list_;
};

vector<Particle*> particles;
CListBox lbSystem;

// the function object is created with the listbox as a parameter

// and gets passed to the for_each function.

// for_each calls operator()(Particle*) on the function object for every Particle* in the vector.

std::for_each(particles.begin(), particles.end(), addStringToListBox(lbSystem));


If there were other things that needed adding to the listbox other than particles you could add more: operator()(Model*), operator()(Label*) etc.

[edited by - petewood on May 28, 2003 5:02:24 AM]

Share this post


Link to post
Share on other sites
Seriema    634
cool

I know how to make functors, I especially love my SafeDelete

I was just hoping that I didn't need to write a functor for adding strings to a list... Am I wrong to make this conclusion: making a functor for this purpose won't increase the efficiency nor the readability of my code?

Because if it doesn't, it isn't worth it

(oh for you curios people out there, having a functor SafeDelete is great! I use it everytime I want to delete a pointer, if it's "alone" or in a container!


        
struct SafeDelete
{
template <typename T>
inline void operator () ( T*& ptr ) const
{
delete ptr;
ptr = 0;
}
};


any comments on my SafeDelete? hmm come to think of it, I think I based it on one I saw in Effective STL by Scott Meyers)

"No lies of sugar can sweeten the sournes of reality"

}+TITANIUM+{ A.K.A. DXnewbie[onMIRC]

[edited by - Seriema on May 29, 2003 7:30:23 AM]

[edited by - Seriema on May 29, 2003 7:35:49 AM]

[edited by - Seriema on May 29, 2003 7:44:25 AM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
I have a few comments on SafeDelete... calling delete on 0 is safe, so you don''t need to check for nullity before you delete. Also, the pointer you set to 0 is a local pointer for that function, which has no effect whatsoever on the pointer you pass to SafeDelete.

Perhaps a macro instead?

Share this post


Link to post
Share on other sites
petewood    819
quote:
I was just hoping that I didn't need to write a functor for adding strings to a list... Am I wrong to make this conclusion: making a functor for this purpose won't increase the efficiency nor the readability of my code?


Well, you don't have to write one - I've written one for you.

I think it is far more readable than writing an explicit loop as in your first example and MKH's solution is only easy to understand if you know what's going on already! Efficiency - all three solutions will evaluate to pretty much the same thing.

Your safe delete isn't any safer by having the check for a null pointer. delete checks that anyway so you are introducing an inefficent extra check. The reason it is safe is because it remembers to set the pointer to zero afterward. Also you don't need the inline as template header functions are inline anyway.

      
struct SafeDelete
{
template <typename T>
void operator () ( T*& ptr ) const
{
delete ptr;
ptr = 0;
}
};

edit: changed to pass by reference (thanks dalleboy)

[edited by - petewood on May 29, 2003 8:48:12 AM]

Share this post


Link to post
Share on other sites
Seriema    634
guess my edit''s didn''t come in time, unless it was you that posted torbjörn?

chatted with a friend on MSN about SafeDelete right after I posted and changed it a bit.

macros... na... they''re generally not wanted and ain''t usefull for std::algorithms

"No lies of sugar can sweeten the sournes of reality"

}+TITANIUM+{ A.K.A. DXnewbie[onMIRC]

Share this post


Link to post
Share on other sites
Seriema    634
petewood: I was under the impression that templates only inline under the same conditions as normal inlines, no loops etc? as always, inlines are only a recommendation to the compiler. It''ll probably inline it anyway (since it''s just two rows), but I put it there more for the coder than the compiler.

I appreciated that you wrote the functor for me. If you guys think it''s more readable I''ll use it. It can be usefull for other cases later on maybe?

thinking of going up the hiearchy a little though, as high as where the function AddString() is introduced in MFC. gonna check on that..

"No lies of sugar can sweeten the sournes of reality"

}+TITANIUM+{ A.K.A. DXnewbie[onMIRC]

Share this post


Link to post
Share on other sites
petewood    819
quote:
daleboy: But only the local copy of the pointer, it will not affect the calling object, unless you change it so it takes a reference to a pointer.


right you are

[edited by - petewood on May 29, 2003 8:47:21 AM]

Share this post


Link to post
Share on other sites
Seriema    634
dalleboy: but I did change mine earlier today, it takes a pointer reference

"No lies of sugar can sweeten the sournes of reality"

}+TITANIUM+{ A.K.A. DXnewbie[onMIRC]

Share this post


Link to post
Share on other sites
flangazor    516
OP said:
  
ORC::ParticleFX::Container::const_iterator i = particleFX.GetSystems().begin(); for(; i != particleFX.GetSystems().end(); ++i) lbSystems.AddString( (*i)->GetName().c_str() );


why not...

  
ORC::ParticleFX::Container::const_iterator it;
for(it=particleFX.GetSystems().begin;
it != particleFX.GetSystems().end();
lbSystems.AddString( (*i)->GetName().c_str()), ++it)
){}
or some other idiom.

Share this post


Link to post
Share on other sites
Seriema    634
what purpose would that serve?

it's possible but... why? it wasn't clearer, and there are no effeciency benefits? *not to rant or anything*

oh by the way, always try to initialize objects instead of first creating them and after assigning a value.

"No lies of sugar can sweeten the sournes of reality"

}+TITANIUM+{ A.K.A. DXnewbie[onMIRC]

[edited by - Seriema on May 29, 2003 9:35:33 AM]

Share this post


Link to post
Share on other sites
civguy    308
I wouldn't use a custom functor or any of those compose/bind functors for this task. Custom functors only make the code more scattered and compose/bind make it harder to understand. There's nothing wrong with the for loop in the original post. for_each isn't really much of an abstraction, that it should be blindly pursued.

If you can't do with few standard functors and the loop code can't be abstracted so that it's useful elsewhere, use a plain old for loop. That's my rule of thumb.

[edited by - civguy on May 29, 2003 11:52:13 AM]

Share this post


Link to post
Share on other sites
daerid    354
The SafeDelete functor is actually quite useful ( I always named mine "deleter" ), especially if you have tons of containers of pointers all over the place.

Although, it''s kind not necessary if you just put containers of boost::shared_ptr''s instead.

Share this post


Link to post
Share on other sites
Ok, I haven't read the whole thread so this could be way
wrong or stated eariler, but here goes.

What you want is BEER!
What you need is BEER!
and no im not drunk...

BEER in this case is "Better Efficency and Encapsulation Repository"
a utility header coded by yours truly to collect usefull
snippets and neat techniques and soon it will be availbile
for public download.

Anyways the parts you wan't are

  
template <typename R, typename C, typename A>
class obj_fun_t
{
typedef R (C::*fun_t)(A);
C *m_pObj;
fun_t m_pFun;
public:
typedef R return_type;
typedef A argument_type;

obj_fun_t(C* pC, fun_t pFun): m_pObj( pC), m_pFun( pFun){}
R operator()(A arg){ return (m_pObj->*m_pFun)( arg);}
};

template <typename R, typename C, typename A>
obj_fun_t<R, C, A> obj_fun(C *pC, R (C::*pFun)(A)){ return obj_fun_t<R, C, A>( pC, pFun);}

template <typename F1, typename F2>
class xform_t
{
F1 m_xlate;
F2 m_work;
public:
xform_t(F1 xlate, F2 work): m_xlate( xlate), m_work( work){}
typename F2::return_type operator()(typename F1::argument_type a){ return m_work( m_xlate( a));}
};

template <typename F1, typename F2>
xform_t<F1, F2> xform( F1 xlate, F2 work){ return xform_t<F1, F2>( xlate, work);}


you know the deal with mem_fun
it transforms from f( obj); to obj->some_func();
what you want here is the bit more involved
f( obj) to class_instance->some_function( obj);

obj_fun solves that =) (there's also a specialication for const member functions but im trying to be terse here)

also since you need to translate the argument you can use xform.

ill show you some code:

  

struct B
{
int f(int i){ cout << i << "\t";}
};

int mul2(int i){ return i + i;}

int main(int argc, char *argv[])
{
vector<int> ivec;
for(int i = 0; i < 10; ++i)
ivec.push_back( i);
B b;
for_each( ivec.begin(), ivec.end(), xform( ptr_fun(mul2), obj_fun( &b, &B::f)));
cout << endl;

system("PAUSE");
return 0;
}


B would be your lbSomethingOrOther
f corresponds to AddString
and mul2 would become something along the lines of
inline const char *WhatsYourName( ParticleFX* thing)
{
return thing->GetName().c_str();
}

hope that helps =)
you got my ICQ if you need anyhelp.

[edited by - DigitalDelusion on May 29, 2003 5:26:01 PM]

Share this post


Link to post
Share on other sites
Sphet    631
I guess when I design a system where I am using STL I normally write functions that are consistent so I can use a templated functor. When I need to do searches for objects, I make sure the objects have an ID or GetID() function so that I can use my ''find_if'' templated functor. Same as the safe delete. Likewise, when working with a widget that needs to have data added, I''ll make sure all my widgets have an ''AddString()'' or ''SetWindowText()'' function so I can write a templated AddString functor. Just a thought to avoid all the headache.

Share this post


Link to post
Share on other sites
Shannon Barber    1681
quote:
Original post by Seriema
petewood: I was under the impression that templates only inline under the same conditions as normal inlines, no loops etc? as always, inlines are only a recommendation to the compiler. It''ll probably inline it anyway (since it''s just two rows), but I put it there more for the coder than the compiler.



Unfortunetly, there are two concepts called ''inline''. The C++ keyword has little to do with what you are talking about. When you declare a function inline, you are telling the compiler that this function is being declared in a header, and not to produce duplicates of it when compiling so the linker does not get confused.

//header.h
void test()
{
//...
}


//source1.cpp
include header.h

//source2.cpp
include header.h

When it links, you''ll get an error about two functions both called test.

inline void test() fixes the problem.


__inline, __forceinline et. al. will give the compiler hints to inline the code (and are not part of the standard).

Share this post


Link to post
Share on other sites
quote:
Original post by petewood
when it's a class member though inline doesn't matter, does it?


I guess you mean this:

class A
{
public
void f(){ /* do stuff */} // implicit inline
};


and that gives you inlining it has the same effect as:


class B
{
public:
inline void g();// explicit inline
};

inline void B::g(){ /* do other stuff */


[edited by - DigitalDelusion on May 30, 2003 4:36:12 AM]

Share this post


Link to post
Share on other sites
antareus    576
This would help in the following way:


char* data = new char[1024];
delete[] data;
// .. lots of code so that we forgot our deletion

for(int i=0; i < 1024; i++)
data[i] = 0; //uhoh, data pointed at deallocated memory


The people writing the safe_delete function want it to delete the memory and change the pointer's value to zero . That way they don't have to worry about setting it to zero manually.

So if we want to modify the address stored in the pointer we must pass it by reference. Without it, setting the argument to zero would affect only the function's pointer, not the caller's.

4th edit: bad day...

[edited by - antareus on May 30, 2003 5:22:07 PM]

Share this post


Link to post
Share on other sites