Objects in C, using blocks

by Chris Barrett

Today I realised that clang supports Apple’s blocks language extension outside Objective-C compilation units. I have assembled the finest killers in the Iron Islands and set sail for the Land of Objects on the fastest schooner in the fleet—the Preprocessor. Hacks ahoy!

Blocks are a recent extension that adds closures to C. See Apple’s documentation for the nitty-gritty. There’s a comparison of C blocks and C++11 lambdas here.

Motivation

I dislike C++. Like, a lot. But alone, on hot summer nights, I find myself lusting after templates and the STL. O sweet std::vector, how I crave the scent of your type safety.  So I set out to implement a type-safe generic container of my own.

In C, generic containers access their elements using void pointers—pointers without compile-time type information. Consuming the contents of such a container requires judicious casting and all the associated runtime risks. Void pointers remove type-safety. We want to do the other thing.

Types in C are not first-class so there’s no way to pass them around—that’s the problem templates solve in C++. That leaves preprocessor macros as the only standard way to parameterize types in C.1

And now, for some code

Below is a toy implementation of an Array class in C, requiring only the blocks language extension.

The Array_new macro initialises a struct that associates an array with some methods,2 creating a simple object. These methods are implemented as blocks which have their types filled in by the preprocessor. For demonstration, I have implemented two simple iteration methods:

  • each – iterates over the array, applying a block to each element
  • each_i – like each, but also provides the index of the element.

Here’s an example program that declares and uses one of these Array objects:

Those sure look like method calls! Once you get used to the syntax for blocks, it reads quite well too.

Here’s the implementation of the Array_new macro:

An Array’s methods are implemented as blocks that capture the struct they are associated with. This simulates the implicit self paramater of methods in object systems and lets us ape the method-call syntax of C++.

An Array’s elements are accessible through fully-typed get and set methods, which are able to perform bounds-checking. In performance critical sections, it’s still possible to access the underlying primitive array.

Discussion

Through this unholy union of preprocessor and blocks, we have simple generic arrays with associated type-safe methods. Hooray!

As an aside, I chose to implement iteration methods because they really showcase the potential of blocks in C. There are benefits to using higher-order functions like these over an equivalent for-loop:

  • The method names make the intent of the iteration clear
  • Passing a block decouples the traversal algorithm from the action at each step, promoting composition.

On the other hand, using methods and blocks for iteration introduces some performance overhead:

  1. a pointer dereference for the initial method call
  2. for each array element:
    1. a pointer dereference to get the block structure
    2. a function call to invoke the block.

So, are these really objects? Not by some definitions; there’s no vtable so there’s no virtual dispatch. But if you’re using C in 21st century, you probably see that as a feature.

  1. I say standard because it’s always possible to write your own code transformation tools. But it’s a slippery slope toward your own home-grown C++ and no-one wants that.
  2. I should really say member functions since these aren’t really methods, but that’s a mouthful.
Advertisements