Nu Blocks and Objective-C Blocks, Part 1
I hope to devote a fair amount of time on this blog to the Cocoa-based programming/scripting languages F-Script and Nu. So to get started, today’s entry is on a little bit of investigation I did into bridging Nu’s native blocks with the Objective-C blocks that Apple introduced with Snow Leopard.
A quick description of blocks for those who don’t know what they are: also called closures, anonymous functions or lambda functions, blocks are a means of creating a chunk of code inside of other code. The power of blocks is that they can reference context information (i.e. local variables) in the surrounding code.
Blocks are common in high-level languages but until now there had been no mainstream implementation of them in the C family of languages. Here is a simple example of an Objective-C block in action copied from one of my projects:
NSMutableAttributedString *fixedText=[unfixedText mutableCopy];
__block id oldAttrName, newAttrName;
__block int checked,fixed;
id attrBlock = ^(id value, NSRange range, BOOL *stop)
{
if (value == nil)
return;
checked++;
NSRange goodRange = [plainText rangeOfComposedCharacterSequencesForRange:range];
if (!NSEqualRanges(range, goodRange))
fixed++;
[fixedText removeAttribute:oldAttrName range:range];
[fixedText addAttribute:newAttrName value:value range:goodRange];
};
//verse
oldAttrName = @"HebrewVerseAttribute",
newAttrName = @"HRVerse";
checked=0;
fixed=0;
[unfixedText enumerateAttribute:oldAttrName
inRange:NSMakeRange(0, [unfixedText length])
options:0
usingBlock:attrBlock];
printf("\nChecked %i verse attributes\nFixed %i\n",checked,fixed);
//segment/sentence
oldAttrName = @"HebrewSentenceAttribute",
newAttrName = @"HRSegment";
checked=0;
fixed=0;
[unfixedText enumerateAttribute:oldAttrName
inRange:NSMakeRange(0, [unfixedText length])
options:0
usingBlock:attrBlock];
printf("\nChecked %i sentence attributes\nFixed %i\n",checked,fixed);
I like this code snippet because it uses a Cocoa method that takes blocks and because it does something that would have been a bit more tedious without blocks. I’m not going to step through the whole thing but here are a few quick notes. Note the ‘__block’ keyword. If you don’t use this keyword a block will still have access to that variable, but only via a copy of it. Hence any changes made to that variable after the block is created will not be reflected in the block. Also, the compiler will raise an error if code in the block tries to change the value of the variable.
Be aware that blocks are Objective-C objects; you might notice I use a variable of type ‘id’ to store the block reference. This is convenient and fine for this situation but you should note that you cannot call a block unless it is specifically cast as a block type. So if I wanted to directly call that block above rather than just passing it on to Cocoa to call, I would have declared the variable like so:
void(^attrBlock)(id,NSRange,BOOL*) = …
You can see why I used ‘id’. It also happens to be the case that the compiler doesn’t like you casting a block to an arbitrary pointer type (e.g. void*, int*, etc), but it has no problem with you first casting it to ‘id’ and then to some other pointer type; a useful thing to know if you want to hackishly write over parts of your block…
So what happens when you call a block? Here is the disassembly of a block call in some x86-64 code:
mov %rax,-0x18(%rbp) //the pointer to the block object is in rax mov -0x18(%rbp),%rax mov 0x10(%rax),%rax //the pointer to the block function is at +0x10 into the block object mov -0x18(%rbp),%rdi //the first argument (this example has no others) //is always the pointer to the block object callq *%rax
Ah! So the block contains a function pointer and when that function is called, the pointer to the block is passed as the first argument. So let’s run a little test:
void foo(id block, int x)
{
printf("thrice %i is %i\n",x, 3*x);
}
int main (int argc, const char * argv[])
{
void(^b)(int) = ^(int x){printf("twice %i is %i\n",x,2*x);};
b(5);
*((void**)(id)b+2) = (void*)foo;
b(5);
};
Guess what this prints out? Cool, huh?
So what’s this *((void**)(id)b+2) monkey business? Well we are casting b to type ‘id’ and then to type ‘void**’. We then offset the pointer by 2 units, remembering that in C when you add n to a pointer it actually adds n times the size of the type the pointer points to. A ‘void**’ points to a ‘void*’ which on x86-64 is 8 bytes long. Twice that is 16 or 0×10 bytes, the correct offset to the function pointer inside of the block object. So I just took another function pointer, making sure it takes the correct arguments, and wrote that into the correct place in the block. And the block still works! Amazing!
So maybe you see where I’m going with this. At the beginning of this post I mentioned that I was trying to bridge Nu blocks and Obj-C blocks. What that means exactly is that I want to be able to use the new Cocoa methods that take Obj-C blocks and call them from Nu code, passing them Nu blocks. So I basically need to generate Obj-C blocks at runtime that will call Nu blocks. But it’s not as simple as it sounds at first. The generated Obj-C blocks need a signature (parameter list) that matches what the Nu blocks expect.
Next post I’ll show you how I did this, mainly by reusing Tim Burks’ code that allows C code to call Nu methods.
Stay tuned for more!
UPDATE:
Part 2!
