blocks and lambdas

hi,

What is to be the relationship between Apple's blocks standard and C++0x lambda functions? Are these things features going to be integrated or interchangeable in clang?

-James

They provide similar functionality, but have many differences. Clang will implement both as independent language constructs. Library code that wants to use both should be able to handle them through std::function (for example).

-Chris

I suppose the following points are the critical points according to my current understanding:

1) integration of lambdas with libdispatch. As a 3rd party commercial developer, I need to be able to write standard code that works across platforms cleanly and efficiently....that means using C++0x Lambda functions rather than blocks. Microsoft has set their parallel APIs up to use lambda functions. Being able to submit lambda functions to the normal dispatch queues is critically important. If I get that ability, I can live with it.

2) memory management. I don't know all the details on the differences, but I what I have read about the differences focuses on memory management of stack variables. You can tell "blocks" to preserve the lifetime of stack variables if they are passed off to blocks that survive the scope of the current function. What I would submit is that for C++ code, if one wants to preserve such variables, he'll be using shared_ptr or some other reference counted smart-pointer. So, it seems to me that you should be able to map a lambda to a block...copying the required stack variables by value and if the programmer does something stupid like pass a reference to a stack variable, then the app will crash and he'll know about it pretty quickly. Given standard practice in C++-land, I wouldn't worry about the __blocks keyword and such with lambdas. The C++ programmer is inherently expected to understand the lifetime of the variables he is passing to the lambda. He will implicitly determine the lifetimes of his variables by how be declares them (shared_ptr, file scope, static, etc) . So, if you don't worry about the __blocks keyword, it seems to me that you should be able to map a lambda into the existing blocks runtime mechanism in a straight-forward way. The reason this feature would be useful is that one could write even his mac-specific cocoa code in terms of lambda functions to keep his codebase more consistent....having everything in lambda syntax rather than mixing the two syntaxes. Using the lambda syntax may indeed allow for additional sharing of code across platforms.

I admit that I am not fully informed on the subject and I'm throwing my thoughts out there for the sake of discussion. If there is some point I'm missing, please feel free to point me to an explanation of it. I'd appreciate the education.

-James

Block pointers should already work as function objects, although it's not totally transparent because (1) block pointer types aren't class types, which is detectable, and (2) copying a block pointer around isn't safe if it hasn't been Block_copy'ed, whereas copying a lambdas should work fine if it doesn't capture anything on the stack by reference.

It would certainly be a neat hack to have an implicit conversion operator from lambda types to the appropriate block type; whether it would copy the block by value or reference is an interesting question.

John.

Is that because Block_copy is changing retain counts on captured values?

No, it's because block literals are initially allocated on the stack, so if you
return out of that stack frame, the block pointer becomes a dangling reference.
You could get the same result by passing a C++0x lambda around by
reference or pointer.

The problem is that generic code written for '0x lambdas will often assume that
copying a function object by value does a deep copy; however, copying a
block pointer is really just a pointer "copy", and the deep copy operation has
to be invoked manually.

John.

What if you build your lambda structure around the existing block structure such that the lambda type would have a copy constructor that calls Block_copy? If the lambda has a type, it stands to reason that it could have a copy constructor.

You seem to be describing a transformation which would occur when a block pointer is treated like a lambda. That's not actually a knowable point in a program.

John.

On 2011-2-8£¬10:33£¬James Gregurich <bayoubengal@mac.com> Wrote£º

What if you build your lambda structure around the existing block structure such that the lambda type would have a copy constructor that calls Block_copy? If the lambda has a type, it stands to reason that it could have a copy constructor.

I think no need of Block_copy operation on C++0x lambda. C++0x support allocate lambda on heap directly.

You can write this:

auto lambda_on_heap = new auto( {
   // your codes here
});

thanks for the info. I find it interesting.

What if you build your lambda structure around the existing block structure such that the lambda type would have a copy constructor that calls Block_copy? If the lambda has a type, it stands to reason that it could have a copy constructor.

You seem to be describing a transformation which would occur when a block pointer is treated like a lambda.

no. not really a "transformation"...more of a syntactic wrapper. you control your compiler and runtime. When clang hits a lambda in C++ code, I assume the backend could generate a block structure to implement the lambda. Also, if the lambda has a c++ type, then it could also have a copy constructor to invoke Block_copy so that the mechanism works normally with stl. Is such an assumption off-base?

That's not actually a knowable point in a program.

I don't understand what you mean by this statement.

-James

What if you build your lambda structure around the existing block structure such that the lambda type would have a copy constructor that calls Block_copy? If the lambda has a type, it stands to reason that it could have a copy constructor.

You seem to be describing a transformation which would occur when a block pointer is treated like a lambda.

no. not really a "transformation"...more of a syntactic wrapper. you control your compiler and runtime. When clang hits a lambda in C++ code, I assume the backend could generate a block structure to implement the lambda. Also, if the lambda has a c++ type, then it could also have a copy constructor to invoke Block_copy so that the mechanism works normally with stl. Is such an assumption off-base?

Okay, this is the opposite direction of interoperability from what I thought you were talking about; this is using a lambda expression as if it were a block. I certainly would like this to work; this is what I was getting at by saying "an implicit conversion to the appropriate block pointer type". We probably won't use the standard block layout, though, because it's a pretty substantial size overhead (4-5 pointers) that most C++ clients won't want. Obviously, when a lambda expression is immediately converted to a block pointer, we'll want to just build it as a block in the first place.

That's not actually a knowable point in a program.

I don't understand what you mean by this statement.

I was talking about the opposite direction of interoperability, i.e. using a block expression or block pointer as if it were a lambda. This is what's unsafe because of the dangling-pointer issue and that we can't really make safe because of how lambdas work.

John.

given the lambda is the cross-platform standard, I think being able to pass the lambda to the Cocoa callback-using APIs and to libdispatch is the useful situation....ie...lambda being able to masquerade as a block and interoperate cleanly with Apple's home-grown infrastructure. If I can write my code entirely in terms of lambda's and that code works with Apple's native stuff, I can't image why I would want to use a block with standard code. If did want to put a block into a STL container or some such thing, I could certainly create a wrapper template that performed the Block_copy.

If you can make that functionality happen, then I suppose that it will meet my needs and I'll be happy.

-James

I'm not sure what this means. Blocks are also a cross-platform solution. We shipped a working blocks runtime as part of Étoilé six months before Apple shipped theirs, and it's now been incorporated into GNUstep. I've used blocks on OS X, FreeBSD, OpenBSD, Solaris, and Linux. Apple (more recently) released their blocks runtime as part of LLVM's compiler-rt project, so you can use blocks without an Objective-C runtime, if you choose.

Blocks are a cross-platform solution, they're just not well supported by other compilers yet[1], but they're quite useable on a variety of platforms.

David

[1] Actually, they're not well supported by clang yet either - we allocate too many structures for __block variables.

-- Sent from my PDP-11

James, clang doesn't even support lambdas at all yet... I think you are putting the cart before the horse.

Also, clang is not in charge of defining Mac Apis. If you want some API to work with lambdas, you should file a bug report at apple's bugtracker.

-Chris

Can you clarify what you mean here? I'm not sure how to allocate fewer structures for __block variables, given how a single variable can be shared between multiple block objects.

John.

He didn't say 'cross-platform solution' he said 'cross-platform
standard', as in ISO standard. Even Objective-C has no formal standard,
let alone blocks.

I'm not interested in stepping on anyone's favorite technology. If you love Blocks and gnustep that is wonderful!

lambdas are part of the C++0x standard. Therefore, it is a mechanism all C++ compiler vendors will eventually support. My company does commercial desktop development and the "cross-platform" that matters to us is Macintosh gcc/clang and Visual-C++. It is highly unlikely that Microsoft will ever support Blocks (they don't even support C99)...particularly given they have already implemented lambda support in VC++. Therefore, it is in my interest to make sure Apple's tools offer first-class support for as many standard C++ technologies as possible so that my development burden is eased. I envision that lambdas will be a big part of my future and I wanted find out what direction clang is taking on lambda support.

-James

point taken. However, I'm sure this list is loaded with apple compiler engineers and I'm here to voice my needs to you guys on the inside in person and early enough in the process to matter. I can indeed file a feature request with Apple. Based on what I am hearing here, I'm confident you guys are on a path that I will be happy with.

-James

A __block variable can be shared between multiple blocks, but it often isn't. We really only need to allocate one block byref structure for each set of variable / block pairs, i.e. if two variables are only referenced by the same (set of) block(s) and no others, then they can be combined. There is a note in the spec to this effect. I looked at implementing it a while ago, but the effort-reward ratio was too high unless I actually find some code where the byref allocation time is a bottleneck. The simplest (and most common) case, where a group of __block variables is only referenced by a single block would be quite easy to improve though.

David

-- Send from my Jacquard Loom

Lambdas and blocks are orthogonal technologies. Blocks are a C extension designed to interoperate nicely with Objective-C and to work with C++. Lambdas are a (very different) C++ technology. Saying that you want a C library (libdispatch) to support a C++ technology (lambdas) doesn't make much sense.

Given the different semantics between blocks and lambdas, implementing one in terms of the other does not really make sense. However, it would be fairly simple at the library level to implement a wrapper for one in terms of the other, so this isn't a compiler issue, it's an issue for your code.

Having blocks and labmdas share an implementation in clang would be problematic for a variety of reasons. For one thing, blocks already have a well-defined ABI, and lambdas will have a per-platform ABI. Unless you could persuade the C++ committee to break with tradition and specify an ABI, and specify that ABI to be compatible with the existing, deployed, blocks ABI, then this is not feasible.

David

-- Send from my Jacquard Loom