//
you're reading...

Programming

Wrapping C-Style Callbacks with Blocks

If you ever done any non-trivial C programming, you’d probably come across this kind of callback API:

void processStuff(StuffCollection* lotsaStuff,int (*callBack)(Stuff* item,void* data),void* data);

198372461_ac2feb164d_m.jpgTypically StuffCollection represents a composite structure that contains one or more Stuff object and the API call processStuff iterates through those objects and invokes your callback function. You can pass some state information into your callback through the opaque data parameter. If you’ve experienced this it’s quite a pain in the rear to package all state information into some data structure and managing that structure’s memory allocation. Even worse if that particular set of state information is only needed once, making the struct that binds them together only litters your code. Not to mention the single-use callback function that is also littering your code.

Handling this kind of function is no better in Objective-C. Sure, you can pass the self pointer — but what if there are more temporal information that the callback needs to handle? You still need to create a special-use struct or class, the callback function, and all the cruft that comes with those.

But this have changed with the advent of Objective-C blocks in Snow Leopard and iOS 4. Blocks are also known as closures and is a convenient way to package snippets of code along with their state information.

How? Simply wrap the API with an equivalent block-based method and you’re done. You need to do this only once per API function, not per instance of you calling the API (like what you probably need to do with the classic C callbacks).

Objective C Blocks Callback

Continuing the example above, for the processStuff function, you’ll write the block-based wrapper:

void processStuffWithBlock(StuffCollection* lotsaStuff, int (^block)(Stuff* item));

Behind the scenes, you’ll define a helper function that will serve as the callback function to the original API call:

int processStuff_helper(Stuff* item, void* data)
{
    int (^block)(Stuff*item) = ((^)(Stuff*item)) data;
    return block(item);
}

Then you’ll wrap the original API function with your block-based function:

void processStuffWithBlock(StuffCollection* lotsaStuff, int (^block)(Stuff* item))
{
    processStuff(lotsaStuff, processStuff_helper, (void*)block);
}

When that’s done, you can simply call the new block-based API like this:

processStuffWithBlock(someStuff, ^(Stuff* item) {
    // … do your processing
    return 0;
});

Much neater and more concise than the plain old C way.

That’s all fine and dandy, you might say, but how about some real example?

Here is one example that I did while using Chipmunk Physics library to code Resonate. Chipmunk Physics is used as a helper to layout Resonate’s word clouds, that makes use of it’s fast bounding-box query functions. In other words, given a collection of rectangles I use Chipmunk Physics to quickly determine whether a new rectangle will overlap an existing one via Chipmunk’s cpSpaceBBQuery function.

Alas cpSpaceBBQuery follows the standard “C” way of doing callbacks: with a callback function and some void* data object. This is the function’s prototype:

void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpLayers layers, cpGroup group, cpSpaceBBQueryFunc func, void *data);

So I wrap it up using blocks like so:

// ChipmunkHelpers.h
 
typedef void (^cpShapeQueryBlock)(cpShape* shape);
 
void cpShapeQueryBlock_helper(cpShape* shape,void* data);
 
static inline void cpSpaceBBQueryWithBlock(cpSpace *space, cpBB bb, cpLayers layers, cpGroup group, cpShapeQueryBlock block)
{
    cpSpaceBBQuery(space,bb,layers, group, cpShapeQueryBlock_helper,(__bridge void*)block);
}
 
// ChipmunkHelpers.m
 
void cpShapeQueryBlock_helper(cpShape *shape, void *data)
{
    cpShapeQueryBlock block = (__bridge cpShapeQueryBlock) data;
    block(shape);
}

Note that in this example I use automatic reference counting – that’s why the __bridge casts are needed.

See how simple it is? Enjoy your new block-based API! ;-) Until next time.

 



Do you enjoy this post? Enter your e-mail address below to receive articles like this one in your mailbox.

Free Updates!

Learn how to grow your indie business while keeping your day job.

Categories

Archives

Keep updated!

Don't miss out on new articles!