Можно ли преобразовать лямбду C ++ 0x в блок clang? - PullRequest
25 голосов
/ 10 ноября 2010

Я задавался вопросом, возможно ли преобразовать лямбду C ++ 0x в блок clang. До сих пор все, что я видел на этом, вовлекало обсуждение их различий. Моя основная причина для изучения этого вопроса - создать возможную оболочку для libdispatch, и, хотя я достаточно осведомлен о dispatch_*_f функциях, никакой информации об их использовании по сравнению с их аналогами блоков было достаточно.

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

Если кто-нибудь знает что-либо, связанное с этим, и может предоставить ссылку, или, по крайней мере, указать мне правильное направление, я был бы очень признателен. (даже ответа «Это в настоящее время невозможно» будет достаточно)

Ответы [ 5 ]

12 голосов
/ 16 февраля 2012

Патч, неявно включающий это преобразование, был только что добавлен в clang trunk.

6 голосов
/ 12 февраля 2012

Обычно, когда лямбда используется для "нисходящего" закрытия, вы можете преобразовать лямбду с ++ в блок clang, преобразовав:

[&](int i) { return i; }

до:

^(int i) { return i; }

Есть еще некоторые тонкие различия. Блоки Clang захватывают только классы C ++ по const. Я не знаю, включает ли это также C ++ POD типы.

Наконец, если необходимо «восходящее» закрытие, тогда они резко расходятся. Блоки Clang требуют, чтобы перехваченные переменные были помечены __block, что компилятор разместит их в куче. Принимая во внимание, что в C ++ способ захвата лямбды должен определяться на основе времени жизни объекта (то есть путем создания копии или путем ссылки).

Также в C ++ копирование замыкания обрабатывается автоматически механизмом конструктора копирования в C ++. Однако для блока clang необходимо вызвать Block_copy и Block_release, чтобы обработать копирование блока. Для этого можно написать простую оболочку на C ++. Например:

typedef void (^simple_block)(void);
class block_wrapper 
{
  simple_block block;

public:
  block_wrapper (const simple_block& x)
  : block(Block_copy(x)) {}

  void operator() () const
  {
    block();
  }

  block_wrapper(const block_wrapper& rhs) 
  : block(Block_copy(rhs.block)) 
  {}

  block_wrapper& operator=(const block_wrapper& rhs)
  {
    if (this != &rhs)
    {
      Block_release(this->block);
      this->block = Block_copy(rhs.block);
    }
    return *this;
  }

  ~block_wrapper () 
  {
    Block_release(this->block);
  }
};
2 голосов
/ 11 ноября 2010

Я не думаю, что фактическое преобразование возможно.В отличие от обратного случая, избавление от исходного блока лязга имеет некоторые побочные эффекты, от которых вы не можете избавиться.В то время как лямбды C ++ 0x могут захватывать переменные по ссылке, ничего особенного не делается, чтобы убедиться, что исходная переменная все еще там, когда вы на самом деле намереваетесь использовать лямбду.С другой стороны, блоки могут взаимодействовать с переменными, объявленными с помощью квалификатора хранилища __block, и в этом случае эти переменные будут храниться в памяти (даже если это означает копирование из стека в кучу) в течение всего срока существования этого блока (включаякопии, сделанные Block_copy):

__ блочные переменные находятся в хранилище, которое совместно используется лексической областью действия переменной и всеми блоками, а также копиями блоков, объявленными или созданными в лексической области действия переменной.Таким образом, хранилище переживет разрушение стекового фрейма, если какие-либо копии блоков, объявленные в фрейме, доживут до конца фрейма (например, будучи помещены в очередь где-то для последующего выполнения).

Поэтому, если вы не собираетесь хранить оригинальный блок (и, следовательно, оборачивать, а не преобразовывать его), некоторые из его первоначальных функций будут отсутствовать, так как переменные __block исчезнут.

Тем не менее, я не специалист по предметам и хотел бы услышать другие мнения:)

0 голосов
/ 12 февраля 2012

Я рекомендую использовать *_f версии функций libdispatch.Все версии блоков реализованы в терминах версий функций под капотом, и гораздо проще написать шаблон C ++, который производит функцию, которая вызывает лямбда-объект, чем шаблон, который создает block , который вызываетлямбда-объект.

Тем не менее, текущая нехватка поддержки в Clang для C ++ лямбда-выражений может привести к снижению эффективности в целом.

0 голосов
/ 14 декабря 2010

Ну, Clang еще не поддерживает лямбды, равно как и Apple GCC.FSF GCC достаточно недавно, чтобы поддерживать лямбды, не поддерживают блоки AFAIK.Так что вопрос о преобразовании между ними пока не применим.

Как только Clang поддерживает оба из них, в режиме ObjC ++ может быть способ выполнить преобразование между ними.

...