Is it possible to convert a C++0x lambda to a clan

2019-02-02 04:03发布

问题:

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)

回答1:

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



回答2:

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


回答3:

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 :)



回答4:

Well, Clang doesn't yet support lambdas, and neither does the Apple GCC. FSF GCCs recent enough to support lambdas don't support blocks AFAIK. So the question of conversion between them doesn't yet apply.

Once Clang supports both of these, there may be a way in ObjC++ mode to convert between them.



回答5:

I recommend using the *_f versions of the libdispatch functions. All the block versions are implemented in terms of the function versions under the hood, and it's far easier to write a C++ template that produces a function that calls a lambda object than a template that produces a block that calls a lambda object.

However, the current lack of support in Clang for C++ lambdas may throw a damper on the whole thing.