Рубиновые блоки, Java-замыкания в C ++ - PullRequest
3 голосов
/ 21 июня 2009

Я занимаюсь разработкой программы, в которой я часто так делаю:

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

У меня есть несколько подобных методов в нескольких объектах, которые в основном применяют свойство ко всем его дочерним элементам. Исходя из мира Ruby, я хочу сделать что-то вроде:

for_all_bodies{ body->SetCollideMode(m) }

Есть ли способ сделать этот код более замыкательным, например или улучшить его?

Я осознаю, как работает C ++, что он основан на стеке, и в нем нет контекстной информации для создания идеального замыкания, подобного функциональности (для этого нужна виртуальная машина?), Но, по крайней мере, по сравнению с текущим повторим этот код в 100 раз программирования.

Ответы [ 5 ]

5 голосов
/ 21 июня 2009

В C ++ 0x, да. См. Здесь. Как вы уже догадались, они выполняются характерным для C ++ способом, то есть если вы случайно закроете переменную стека, а затем позволите лямбда-объекту выжить дольше, чем стек, то у вас есть неопределенное поведение. Это совершенно новый способ аварийного завершения вашей программы! Но это несправедливо - во многих отношениях они более изощренны, чем лямбды во многих других языках, потому что вы можете объявить, в какой степени им разрешено изменять состояние.

До тех пор, были попытки подражать тому же самому , но они, вероятно, больше проблем, чем они стоят.

4 голосов
/ 21 июня 2009

Есть несколько подходов, но ни один из них не идеален.

Во-первых, «традиционный» подход заключается в определении функтора, который делает то, что вам нужно:

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

Это не экономит много кода, но позволяет отделить итерацию от операции, которую вы хотите применить. И если вам нужно установить коллизидный режим несколько раз, вы, конечно, можете повторно использовать функтор.

Более короткая версия возможна с библиотекой Boost.Lambda, которая позволит вам определить встроенный функтор. Я не могу вспомнить точный синтаксис, так как я не часто использую Boost.Lambda, но это будет примерно так:

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

В C ++ 0x вы получаете языковую поддержку лямбда-выражений, позволяющую использовать синтаксис, подобный этому, без использования сторонних библиотек.

Наконец, Boost.ForEach может быть опцией, допускающей такой синтаксис:

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);
  }
}
4 голосов
/ 21 июня 2009

BOOST_FOREACH (или новый цикл, основанный на диапазоне) - это, вероятно, путь, но вот как я обычно подхожу к лямбде в текущем стандарте, используя 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));
}
2 голосов
/ 21 июня 2009

Вы можете использовать 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);
  }
}
1 голос
/ 22 июня 2009

C ++ пока не поддерживает лямбда-выражения. Я иногда использую этот обходной путь:

#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));
}

Только мои 2 цента ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...