Use Objective-C Classes in C++ Opaquely, Readably and Safely

Also note: I wrote an updated post on this topic. Basically just ignore the rest of this article and follow this link instead.

One of my many pet projects currently involves mixing C++ and Objective-C a lot (I am experimenting with porting a cross platform C++ windowing library to OS X). While this is generally not a problem, anybody who has tried to wrap some Mac/iPhone functionality that is exposed via the Cocoa/AppKit/UIKit/… APIs should be familiar with the following problem:

Let’s assume you built a command line speech synthesizer using NSSpeechSynthesizer, that just speaks every command line parameter given to it.

Now imagine you are casually contributing to a library a friend of yours is building and you both decide it would be cool to have this functionality built into this library. Unfortunately it is a C++ only library, so you have to wrap all the Objective-C specifics into a nice C++ class. It could look like this (lets call this file speaker.hpp from now on):

OK, so we have wrapped the AppKit calls in a C++ class, but what happens if someone wants to use this class? They are going to include our header speaker.hpp, of course. And with this they will import AppKit.h,an Objective-C header, which means that their compiler will have to understand this language.

What we actually want, though, is to make the usage of Objective-C in the background completely opaque to the user of our C++ library. The first attempt to do this, that one might come up with, is to use void * instead of NSSpeechSynthesizer in our header. This seems perfectly legal as the Objective-C method-call (actually message-sending) syntax is not limited to Objective-C interface pointers; you can send a message to a void * and if that pointer happens to point to an instance of an Objective-C interface offering the called method, everything will work fine (even if the pointer will point to NULL or nil, the App will not crash; nothing will happen though and if your pointer points to garbage data the behavior is undefined).

Lets just try it: We remove the import statement for AppKit from speaker.hpp and move it to the implementation file. Also in speaker.hpp we change line 30 (previously 31) to

void * const synth_;

and compile. If you are using clang, you will see the problem of this approach:

As said: The compiler will let us do this (it “just” generates warnings), but we lose type-safety. The compiler will no longer generate warnings or errors if we send invalid messages to our NSSpeechSynthesizer. We could even write

[synth_ crashTheApp];

in speaker.mm and it would compile (and would actually even crash the program … ain’t I funny :P). The second issue with this approach is lacking readability, of course. void * says nothing about what synth_ actually is.

So what is the right way to do this then? Well, one way is of course the (in-)famous Pimpl-Pattern. This I don’t want to cover here. I want to present a slightly simpler way of doing it; without the overhead of a private implementation pointer. If you are using Pimpl anyway, though, then you won’t need the following “hack”.

What we are going to do is to create a new type – lets call it your::friends::library::speech::synthesizer – which will be determined by whether we compile our library — where we can use Objective-C all we like — or whether we are compiling a C++ source file that just includes our header. This will of course boil down to some preprocessor magic. We are going to define our new type in a new header file synthesizer.hpp:

The #ifdef __OBJC__ is a pattern you see for example in the “prefix header” that Xcode generates for every project; __OBJC__ is only defined when you compile an Objective-C source file (file extension .m and .mm for Objective-C++). When we are compiling an Objective-C source file anyway (as we do when building our library) we use the proper AppKit class to get type safety. When we want to compile a file as pure C++ (file extension .cpp, .cxx, .c++, ...) we use void. Defining a type as void may seem awkward, but we will always only be using pointers of our new type synthesizer (as there is no such thing as stack allocated instances of Objective-C classes). This also alleviates the fear of different layouts of our class speaker depending on our source file type: Pointers always have the same size, regardless of their type. However, if that bugs you, feel free to just include the little “*” in the two typedefs.1

Notice that a standard forward declaration of the form

class NSSpeechSynthesizer;

in speaker.hpp does not work. On my system it fails with the following error:

In file included from /System/Library/Frameworks/AppKit.framework/Headers/AppKit.h:68,
from /Users/julian/Documents/Blog/objcpp/speaker.mm:2:
/System/Library/Frameworks/AppKit.framework/Headers/NSSpeechSynthesizer.h:44: error: ‘NSSpeechSynthesizer’ redeclared as different kind of symbol
/Users/julian/Documents/Blog/objcpp/./speaker.hpp:3: error: previous declaration of ‘struct NSSpeechSynthesizer’

For further study I put the library we just developed up as a Gist, along with a small program using it (basically the first code snipped) without having to be compiled as Objective-C. I hope I’ll be forgiven for the lack of a proper directory structure; Gists don’t support folders.

1 Actually the C++ standard only guarantees that pointers to layout compatible types have the same value represenation and alignment requirements, so the two classes could actually end up having different representations in memory. I don’t yet know what implications this has, but if you want to be on the safe side, just use Pimpl.