
Objective-C Blocks vs. C++0x Lambdas: Fight  - leakybucket
http://www.mikeash.com/pyblog/friday-qa-2011-06-03-objective-c-blocks-vs-c0x-lambdas-fight.html
======
comex
> One place where useful optimizations could be made are inline functions
> which take block parameters, since the optimizer is able to improve the
> inlined code based on the calling code. However, as far as I know, no
> current blocks-capable compilers perform any of these optimizations,
> although I haven't investigated it thoroughly.

> [...]

> for_each is a template function, which means that it gets specialized for
> this particular type. This makes it an excellent candidate for inlining, and
> it's likely that an optimizing compiler will end up generating code for the
> above which would be just as good as the equivalent for loop.

This is somewhat unfair, as it seems to reflect the common misconception that
(e.g.) C++ sort is faster than C qsort because it uses templates, when in fact
qsort would be just as fast if its implementation were written in the .h file,
as C++ sort's is. Compilers are perfectly capable of inlining calls to
function pointers.

Calls to blocks should be able to be inlined too, but I guess they're still a
new feature; I did a quick test, and it seems that gcc cannot inline them, but
llvm-gcc and clang can.

In this case, the article suggests:

    
    
       [array do:^(id obj) {
           NSLog(@"Obj is %@", obj);
       }];
    

Objective-C message calls are never inlined since they are dynamic, but if you
write something like:

    
    
      static void for_each(NSArray *array, void (^callback)(id)) {
         for(id obj in array) {
             callback(obj);
         }
      }
      [...]
      for_each(array, ^(id obj){ NSLog(@"%@", obj); });
    

llvm-gcc and clang are able to generate code equivalent to doing the for loop
directly.

~~~
ot
> Compilers are perfectly capable of inlining calls to function pointers.

Does this happen even if the function that calls the callable (in this case
_sort_ ) is not inlined? This would mean that the compiler generates a
specialized function, say sort_{somerandomnumber} with the user-defined
callable inlined into, and this seems kind of unlikely to me (while with
templates the compiler is _forced_ to do so).

In both your example the body of the caller ( _array do_ and _for_each_ ) is
so small that I assume it is inlined.

~~~
comex
No, but you could get the same effect by writing a short wrapper function and
getting sort inlined into that. (Might be difficult in some cases, but I could
imagine a "qsort_efficient" which is declared __attribute__((always_inline)).)

------
mustpax
This really captures the core difference between the two languages. C++ is
overly versatile to the point of being cumbersome whereas Obj-C imposes
sensible limitations for the sake of handling the common use cases much
better.

~~~
cageface
Or, C++ gives you speed and low-level control at the cost of some extra
complexity.

~~~
iam
There's hardly extra complexity in C++0x lambdas, you just need the & or =
character to specify if variables are captured by value or by reference and
off you go. You can even save the closure as an auto variable instead of
needing to spell out the type! Seems easier to me.

Personally I prefer the C++0x closures precisely because of the
reference/value capturing distinction.

~~~
pilif
As far as I understood the article, using references is a desaster waiting to
happen as accessing the references once the original variables are out of
scope is "undefined behaviour" - probably accessing random values on the stack
if your reference was pointing to a variable on the stack

~~~
shin_lao
Not using references doesn't help you at all, you have to recursively make
sure the lifetime of all the objects you use is greater than the lifetime of
your closure.

------
calloc
I don't necessarily consider this a fight at all, I think each has their
strengths and weaknesses. I much prefer the C++ method, however when
programming in Objective-C using blocks just feels more natural.

~~~
barrkel
The C++ method doesn't give you a trivial way of capturing variables rather
than values, where those variables go on to have a life that matches the
lifetime of the closure created. You have to work rather hard to get that, by
wrapping things up in little holders which are captured by value, but don't do
a deep copy when they themselves are copied. It reminds me a little of how
arrays are sometimes used to capture final variables in a mutable way in Java
anonymous inner classes.

What this means is that whenever you pass off a lambda to a function, and that
lambda captures a variable, you need to be aware of whether or not the
function keeps a reference to the lambda someplace, so that you'll know if
it's safe to capture by reference or not. If you capture by reference but the
function keeps a reference to the lambda, and it gets called after the
captured variable has gone out of scope, you'll get into some nasty trouble.

And this is why I don't like the C++ spec as it stands. It requires a kind of
global knowledge to work with correctly in local contexts, in such a way that
the compiler can't really help you either (it may have been possible to
annotate types to indicate closure lifetime, but it would be painful without
more powerful type inference than C++ has).

~~~
bitwize
You simply can't do upward funargs in a C-family language without breaking the
memory and activation-record model of the language. For example, let's say C++
had upward funargs. Any time you return a function value, or otherwise keep it
around for longer than the lifetime of the function activation in which it is
activated, any free local variables in the closure which are captured by the
containing environment _must_ refer to locations on the heap. (Copying them
by-value breaks the semantics of closures.) This conflicts with the assumption
in C++ that auto variables local to a function activation are part of the
function's activation record on the stack.

You could get around this problem with spaghetti stacks or something, but
you'd need to find a way to free the activation records that are floating
around after their enclosing scopes have expired -- enter garbage collection
which you do NOT want to require for C++.

That's the problem with Lisp, it's almost all-or-nothing. If you want to
correctly include some of the benefits of the Lisp execution model -- like
lambdas -- you need to accept the whole thing, lock, stock, and barrel.
Including heap-allocated local vars and the garbage collector. (And yes --
Python, Ruby, Haskell, and Standard ML have a "Lisp execution model" in this
sense.)

So we get compromises and hulking abominations like C++0x lambdas or -- worse
yet -- their predecessor, Boost Lambdas.

tl;dr: Upward funargs are hard, let's go shopping.

~~~
ori_b
You can do manual closure management.

    
    
        int f() {
            int x = 42;
            return dupclosure(^(){return x+10}};
        }
         int g()
         {
              int (^fn)();
    
              fn = f();
              fn();
              freeclosure(fn);
         }
    

It's not quite as pretty, but it's workable, and in line with a C-family
language's semantics.

~~~
seabee
However it does require severe adjustments to the lifetime rules for automatic
variables, and you have to be mindful of e.g. custom allocators.

~~~
ori_b
Custom allocators are relatively easy handle with an API something like:

    
    
         void* closureheap(void (^)())
         size_t closuresize(void(^)())
    

Or, well, anything else that allows you to separate the step of allocating
memory from it's initialization. (There are probably more representation-
independent APIs that would be better, but this was just off the top of my
head)

And, yes, mutable captured variables will not be shared across multiple
duplications. Somewhat unconventional, but given the mental model of copying
closures, I don't think it's a surprising behavior.

------
saurik
Objective-C's blocks would be a million times more interesting to me if they
allowed for runtime type inspection: given that they are already fully fledged
Objective-C objects, the fact that they don't have some kind of
"methodSignature" selector that returns the type code is simply confusing. If
they had this feature, then they would be fully usable from dynamic languages
(such as my Cycript, but also things like PyObjC, Nu, etc.) without having to
manually specify the types everywhere (which is error prone and annoying,
especially if all of the rest of your code is working just fine with no types
specified at all). Without this feature, they seem to just be a really limited
version of C++0x lambdas tied to Objective-C's reference counting semantics.

------
Johngibb
Boy, do I prefer how lambdas in C# work.

------
stonemetal
The article is more interesting than the juvenile title makes it sound.

tldr: Lambdas provide more flexibility but are more complicated to use. Blocks
integrate with objective-C better and are simpler to use.

~~~
mikeash
The title is tongue in cheek, basically a riff on how many people consider C++
and Objective-C to be mortal enemies.

