Подтвердить что ты не робот

Можно ли преобразовать lambda С++ 0x в блок clang?

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

в

^(int i) { return i; }

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

Наконец, если требуется "восходящее" закрытие, то они резко расходятся. Блоки Клана требуют, чтобы зафиксированные переменные были аннотированы с помощью __block, который компилятор выделил бы в куче. Принимая во внимание, что в С++ способ захвата лямбда должен решаться на основе времени жизни объекта (то есть путем создания копии или ссылки).

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

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

Ответ 3

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

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

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

Однако я не эксперт по предметам и хотел бы услышать другие мнения:)

Ответ 4

Ну, Кланг еще не поддерживает лямбда, и Apple GCC также не работает. FSF GCC, достаточно недавние, чтобы поддержать lambdas, не поддерживают блоки AFAIK. Поэтому вопрос об конверсии между ними пока не применяется.

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

Ответ 5

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

Однако нынешняя нехватка поддержки в Clang для С++ lambdas может бросить демпфер на все это.