PLBlocks and qsort_b
One of the developer technologies introduced in Snow Leopard is an extension to C/Objective-C called “blocks”, which adds anonymous (lambda) functions to the language. If you’re not yet familiar with blocks, I recommend Mike Ash’s excellent introduction to the subject.
Unfortunately for developers targeting platforms other than 10.6 (including the iPhone), support for blocks in not available from Apple. There is an alternative, however, thanks to the clever folks at Plausible Labs in the form of Plausible Blocks:
Plausible Blocks (PLBlocks) provides a drop-in runtime and toolchain for using blocks in iPhone 2.2+ and Mac OS X 10.5 applications. Both the runtime and compiler patches are direct backports from Apple’s Snow Leopard source releases.
PLBlocks is great, but one thing it can’t provide is block support in
common libraries, as is found in Snow Leopard. For example, the qsort
standard library function has a qsort_b variant under Snow
Leopard that allows passing a block instead of a function pointer for
the comparison operator. From the man pages:
The
heapsort_b(),mergesort_b(), andqsort_b()routines are like the corresponding routines without the _b suffix, expect [sic] that thecomparcallback is a block pointer instead of a function pointer.
The qsort_b function would have been handy for a sorting
task I was implementing on the iPhone, but of course it wasn’t
available. I thought for a while about I might go about implementing a
replacement without reimplementing the whole qsort
function. In the end I was able to, but only through the use of some
undefined behavior and an unexpectedly useful qsort_r
variant.
The qsort_r function takes a void* thunk
that is passed unmodified as context to each invocation of the
compar function.
void qsort_r(void *base, size_t nel, size_t width, void *thunk, int (*compar)(void *, const void *, const void *));My implementation relies on passing the block pointer in the thunk
parameter, which is undefined behavior (passing a function pointer
through a void type and back is undefined, so I assume the
same is true of a block pointer), but it works.
typedef int (^ds_compare_block_t)(const void*, const void*);
int ds_qsort_block_adapter(void* cmp, const void* a, const void* b)
{
return ((ds_compare_block_t)cmp)(a, b); // this is undefined behavior
}
void ds_qsort_b(void* base, size_t nel, size_t width, ds_compare_block_t cmp)
{
qsort_r(base, nel, width, cmp, ds_qsort_block_adapter);
}Of course this is a very specific solution to the case of
qsort (mergesort and heapsort
would be out of luck). A more general solution to the problem of passing
a block (via some adapter) to existing C library functions would be
welcome, but I’m not sure it’s even possible.