tags:

views:

154

answers:

5

I'm making a simple crime sim game.

Throughout it I keep doing the same thing over and over:

// vector<Drug*> drugSack;
for (unsigned int i = 0; i < this->drugSack.size(); i++)
            this->sell(drugSack[i]);

Just one example. I hate having all these for loops all over the place omg QQ, anyway to do something like:

drugSack->DoForAll((void*)myCallBack);

I'm not well versed in the STL.

+1  A: 

Well, first I'm confused: what's sell? Is it meant to be a member function of some class, you need to make drugSack that class, in which case you can do something like the following --

Something like for_each to iterate over drugSack, combined with mem_fun to get sell:

for_each(drugSack.begin(), drugSack.end(), mem_fun(&Drug::sell))

If sell is just an ordinary function, you can just put it in the third argument of for_each.

Andrew Jaffe
This won't work; `vector` has no such method. (`sell`)
GMan
Good idea; however, I doubt that the `vector<Drug*>` will have a `sell` member...
xtofl
right, I misunderstood... I think xtofl has the right version.
Andrew Jaffe
+2  A: 

You could use std::for_each from the STL which applies a function to a range. See the following description: http://www.cplusplus.com/reference/algorithm/for_each/.

You will also need to use std::mem_fun or std::mem_fun_ptr to obtain the member function of your class.

For more advanced cases, have a look at Boost Bind which provides an advanced binder for creating function objects.

Yukiko
+7  A: 

Time to start knowing the stl algorithms:

#include <algorithm>

...

std::for_each( drugSack.begin(), drugSack.end(), 
  std::bind1st( std::mem_fun_ptr( &ThisClass::Sell ), this ) );

The idea is to create an object, called a "functor", that can do a certain action for each of the elements in the range drugSack.begin(), drugSack.end().

This functor can be created using stl constructs like mem_fun_ptr, resulting in a functor taking a ThisClass* and a Drug* argument, and a wrapper around it that will substitute/bind the Class* for this.

xtofl
You forgot the second argument to `bind1st`.
GMan
@GMan: thanks; corrected it.
xtofl
... and in general look at Boost Lambda Library.
dirkgently
... or the boost::bind library (c++0x bind will be close to boost::bind, IIRC)
David Rodríguez - dribeas
... the important bit being the word "close" and the revolting syntax they have for lambdas! Add to that the fact that C++1x (this is what I *think* it is called nowadays) compatibility is a bit of red herring. Hence, my intentional omission of the standard. HTH.
dirkgently
@dirkgently. Some people are calling it C++1x, but since C++0x was only ever a nickname you can (if you want) call it "that stupid proposed new version of C++ that I hate, what was wrong with C++98?" and be equally correct ;-)
Steve Jessop
+6  A: 

Honestly, C++ is currently pretty bad at this kind of stuff. It can definitely do it, as outlined in xtofl's answer, but it's often very clumsy.

Boost has a for-each macro that is quite convenient:

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

// ...

foreach(Drug* d, drugSack)
{
    sell(d);
}

Or perhaps Boost.Bind, though this is slightly more complex, it reads very nice for your case:

#include <boost/bind.hpp>

// ...

// ThisClass refers to whatever class this method is in
std::for_each(drugSack.begin(), drugSack.end(),
                boost::bind(&ThisClass::sell, this, _1));

Bind will make a functor that calls the member function of ThisClass, sell, on the instance of the class pointed to by this, and will replace _1 with the argument it gets from for_each.

The most general method is with lambda's. Boost has a lambda library. I won't include samples here because for your specific case boost bind works, and the lambda's would be the same code. That said, lamba's can do much more! They basically create in-place functions (implemented as functors), but are much more complex to learn.

Both for-each and bind are far cleaner than the "standard" C++ methods, in my opinion. For now, I'd recommend, in order: for-each, bind, standard C++, lambda's.

In C++0x, the next C++ standard, all this will be nice again with built-in lambda support:

std::for_each(drugSack.begin(), drugSack.end(),
                [this](DrugSack* d){ sell(d); });

Or the new range-based for loops:

for(DrugSack* d : drugSack)
{
    sell(d);
}

But we must wait a couple years before this is an option. :( Also, I think the range-based for-loop is the easiest thing to read. This is why I recommend boost for-each, because it mimics this behavior and syntax (mostly).

Also, totally unrelated: the style where you include this-> before everything is, in my experience, generally considered bad practice. The compiler will do it for you, all you're doing is cluttering up your code and introducing the chance of mistakes. Things read much better without it.

GMan
"make this all nice" - for low values of 'nice' :-) I think I prefer the for-loop to all these, except perhaps the boost foreach macro.
anon
@Neil: Pfft, lambda's are like the cool hip thing to do. Actually, I too would prefer the range-based for loop. Less typing for the same end result, and easier to read. I think I'll add that to make us happy.
GMan
Eric Muyser
@Eric: Oops, thanks. I don't know how I missed the OP's comment before. Also, while you're correct in your method of passing a pointer by const-reference, it's preferable to pass fundamental types by value. The compiler would have optimized it to the same code in either case, but since the plain pointer makes it easier to read, we'll stick with that.
GMan
+1  A: 

For the simple of case of looping through an entire container, I'd just write the loop. It's unfortunately long-winded, but not prone to mistakes if you always write it the same way. I always write such loops as follows:

Container c;
for (Container::iterator i = c.begin(), end = c.end(); i != end; ++i)
    ...

(or const_iterator where appropriate).

You could try BOOST_FOREACH as an alternative.

James Hopkin
If `BOOST_FOREACH` isn't an option, I would definitely recommend making a simple custom utility macro.
GMan
@GMan Perhaps. Although you could name such a macro to make its meaning obvious, I bet most programmers would still go looking for its definition to make sure. The code I wrote should be immediately understandable.
James Hopkin
visitor
@visitor Yep, BOOST_FOREACH can give you a fright if your IDE pops up its definition in a tool-tip :-)
James Hopkin
@visitor: Oh, I know how complex it is. :P WHat I meant was the code James has in his code. Make the macro replace `c` with a container name. (And replace `Container` with the container name, for the iterator). Simplest would probably to make two of those, one for `const` or not.
GMan