views:

321

answers:

5

I am developing a program where I find myself doing this like this a lot:

void Model::SetCollideMode( const std::string &m )
{
  Body *body;

  std::map<std::string, Body* >::iterator iter;

  for (iter=this->bodies.begin(); iter!=this->bodies.end(); iter++)
  {
    body = iter->second;

    body->SetCollideMode( m );
  }
}

I have several methods like that in several object that basically apply a property to all its children. Coming from Ruby world I am dying to do something like:

for_all_bodies{ body->SetCollideMode(m) }

There is anyway to make this code more closures like or in other way improve it?

I am conscious of how C++ works, that it is stack based and there is no context information to create a perfect closure like functionality (this need a VM?) but at least improve over the current repeat this code 100 times kind of programming.

+5  A: 

In C++0x, yes. See here. Just as you've guessed, they are done in the characteristic C++ way, i.e. if you accidentally close over a stack variable and then let the lambda object survive longer than the stack, then you have undefined behaviour. It's a whole new way to make your program crash! But that's unfair - in many ways they are more sophisticated than lambdas in many other languages, because you can declare the extent to which they are allowed to mutate state.

Until then, there have been attempts to emulate the same thing, but they're probably more trouble than they're worth.

Daniel Earwicker
"It's a whole new way to make your program crash!" I consider it an exciting innovation of a tried-and-tested way of making your program crash, by creating a dangling reference to a stack variable. You "just" need to remember that closures, along with return statements and functions params of pointer or reference type, have the potential to let references escape their storage duration.
Steve Jessop
+2  A: 

You could use the Boost.Foreach:

#include <boost/foreach.hpp>

void Model::SetCollideMode(const std::string &m)
{
  typedef pair<std::string, Body*> body_t;
  BOOST_FOREACH (body_t& body, bodies)
  {
    body.second->SetCollideMode(m);
  }
}
Ferruccio
I think it would be a loop over pair<std::string, Body*>
Todd Gardner
That's true, I forgot it was a map.
Ferruccio
Jamie Cook
+4  A: 

BOOST_FOREACH (or the new range based loop) is probably the way to go, but here's how I normally approach lambda in the current standard, using tr1 bind:

#include <algorithm>
#include <functional>
using namespace std;

void Model::SetCollideMode( const std::string &m )
{
  for_each(bodies.begin(),bodies.end(),
           tr1::bind(&Body::SetCollideMode,
                     tr1::bind(&pair<std::string, Body*>::second, _1), m));
}
Todd Gardner
oof, the bind syntax always makes my head hurt. I usually settle for writing separate functors instead. Neither solution is pretty though. Looking forward to 0x's built-in support for lambdas. :)
jalf
This is really nasty.. I can't wait for C++0x lamda's!
StackedCrooked
It's certainly an acquired taste :), but after getting used to the style, I like better it better than custom functors for most situations (It gets even messier with overloaded functions or conditionals)
Todd Gardner
+4  A: 

There are several approaches, none of them perfect.

First, the "conventional" approach would be to define a functor to do what you need:

struct SetCollideModeFunc {
  SetCollideModeFunc(const std::string& m) : m(m) {}
  void operator()(std::pair<std::string, Body*>& p) {
    Body* b = p.second;
    b->SetCollideMode(m);
  }

  const std::string& m;
};

void Model::SetCollideMode( const std::string &m )
{
  std::for_each(bodies.begin(), bodies.end(), SetCollideModeFunc(m));
}

This doesn't save you a lot of code, but it does allow you to separate the iteration from the operation that you want to apply. And if you need to set collidemode multiple times, you can reuse the functor, of course.

A shorter version is possible with the Boost.Lambda library, which would allow you to define the functor inline. I can't remember the exact syntax, as I don't use Boost.Lambda often, but it'd be something like this:

std::for_each(bodies.begin(), bodies.end(), _1.second->SetCollideMode(m));

In C++0x, you get language support for lambdas, allowing syntax similar to this without having to pull in third-party libraries.

Finally, Boost.ForEach might be an option, allowing syntax such as this:

void Model::SetCollideMode(const std::string &m)
{
  BOOST_FOREACH ((std::pair<std::string, Body*> p), bodies) // note the extra parentheses. BOOST_FOREACH is a macro, which means the compiler would choke on the comma in the pair if we do not wrap it in an extra ()
  {
    p.second->SetCollideMode(m);
  }
}
jalf
Don't forget BOOST_FOREACH is a macro. That comma in pair type will drive the compiler insane.
Ferruccio
Good point. Fixed :)
jalf
I think boost_foreach is the most usable of the solutions...
Jordi
just be aware of what Ferruccio mentioned. It is a macro, so you have to be really careful when using it.
jalf
Nikolay Vyahhi
fair enough. That's one of the reasons I usually don't use it. It's just too fragile for my tastes. ;)
jalf
+1  A: 

C++ does not yet support lambda's. I sometimes use this workaround:

#include <boost/bind.hpp>
void Model::SetCollideMode( const std::string &m )
{    
  typedef std::map<std::string, Body* > Bodies;
  struct Helper
  {
      static SetCollideMode(const std::pair<std::string, Body*> & value,
                            const std::string & m)
      {
          value.second->SetCollideMode(m);
      }
  };

  for_each(bodies.begin(),
           bodies.end(),
           boost::bind(Helper::SetCollideMode,_1, m));
}

Just my 2 cents..

StackedCrooked
Are you sure you've ever done that for real? You can't refer to a local variable of the enclosing function in a static member function of a local type (in this case your 'body' pointer). Why not just have: value->second->SetCollideMode(m);
Daniel Earwicker
Oops, yeah, indeed that won't work. Indeed, value->second->SetCollideMode(m); will work. I used the 'body' variable because I wanted to preserve the original example as much as possible (failing to realize I couldn't access it in that scope).
StackedCrooked