Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to convert a C++0x lambda to a clang block?

Tags:

I've wondered if it is possible to convert a C++0x lambda to a clang block. So far anything I've seen on it has involved the discussion between their differences. My primary reason for looking into this, is to make an eventual wrapper for libdispatch, and while I'm quite aware of dispatch_*_f functions, any information on their use has been quite lacking, in comparison to their block counterpart.

So far I've been able to find information on converting a C++ lambda to a function pointer, but this is more in the realm of the reverse.

If anyone knows anything related to this, and could provide a link, or at least point me in the right direction, I would really appreciate it. (even a "This is not currently possible" answer will suffice)

like image 328
bruxisma Avatar asked Nov 10 '10 19:11

bruxisma


3 Answers

A patch enabling this conversion implicitly was just added to clang trunk.

like image 162
coppro Avatar answered Oct 06 '22 11:10

coppro


Generally, when lambda is used for "downward" closure, you can convert a c++ lambda to a clang block by converting:

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

to:

^(int i) { return i; }

There is still some subtle differences. Clang's blocks only capture C++ classes by const. I don't know if this includes C++ POD types too.

Finally, if "upward" closure is needed, then the two diverge drastically. Clang's blocks require variables captured to be annotated with __block, which the compiler will allocate it on the heap. Whereas, in C++, the way the lambda captures, needs to be decided based on the lifetime of the object.(That is by value making a copy or by reference).

Also in C++, copying the closure is handle automatically by the copy-constructor mechanism in C++. However, with clang's block, Block_copy and Block_release need to be called to handle the copying of the block. A simple wrapper can be written in C++ to handle this. For example:

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);
  }
};
like image 44
Paul Fultz II Avatar answered Oct 06 '22 10:10

Paul Fultz II


I don't think an actual convert is possible. Unlike the reverse case, getting rid of the original clang block, has some side effects that you can't recover from. While C++0x lambdas can capture variables by reference, nothing special is done to make sure the original variable is still there when you actually intend to use the lambda. Blocks on the other hand, can interact with variables declared with the __block storage qualifier, in which case these variables will be kept in memory (even if it means being copied from stack to heap) for as long as that block lives (including copies made by Block_copy):

__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or created within the variable’s lexical scope. Thus, the storage will survive the destruction of the stack frame if any copies of the blocks declared within the frame survive beyond the end of the frame (for example, by being enqueued somewhere for later execution).

Therefore unless you intend to keep the original block around (and thus wrapping rather than converting it), some of its original functionality will be missing as the __block variables will be gone.

However, I'm no expert on the subjects and would love hearing other opinions :)

like image 30
yonilevy Avatar answered Oct 06 '22 11:10

yonilevy