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!
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.
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:
- a pointer dereference for the initial method call
- for each array element:
- a pointer dereference to get the block structure
- 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.
- 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. ↑
- I should really say member functions since these aren’t really methods, but that’s a mouthful. ↑