Revisiting Using Objective-C-Objects in C++ Classes in the Age of ARC

Back in March 2011 I wrote an article about mixing Objective-C and C++, specifically about using Objective-C object type members in C++ classes. This article even got tweeted by @mrfungfung (Tak Fung of Supermono Studios the developer of one of my favourite iPhone apps: Epic Win). Now, over one year later, Apple introduced a major new feature into its language of choice: Automatic Reference Counting or ARC for short. This new feature forces me to apologise for and “correct” what I said back then. As it turns out, there is actually something to the doubts I was already hinting at in the footnote to that post.

Let me quickly reiterate the problem: Objective-C is a strict superset of C, which can also be imposed on C++, resulting in the dialect called Objective-C++. This means that you can freely mix Objective-C and C++ code; you can even have Objective-C objects (i.e. pointers to them) as members in a C++ class (and vice versa, but thats not what this article is about). The problem is this: Since a standard C++ compiler does not know about the Objective-C type system, every C++ header file that declares a class having such a member variable and every file including such a header file has to be compiled as Objective-C++, not “pure” C++. This is undesirable for example when you want to wrap some functionality from Apples Cocoa Framework (which is written in and for Objective-C) in a C++ only library.

The article linked above presented a solution to this using the preprocessor both Objective-C and C++ use. Let’s say we have an Objective-C class called Cat:

#import <Foundation/Foundation.h>

@interface Cat : NSObject
{
}

@end

We want to use this Cat class in a Person class, which will be written in C++. We also want the Person.hpp header to be compilable by a pure C++ compiler. Objective-C compilers define the preprocessor constant __OBJC__; C++ compilers don’t, so we can use this to conditionally define an “alias” for our Cat type, as my old article suggested. We might come up with something like this:

#ifndef PERSON_HPP_INCLUDED
#define PERSON_HPP_INCLUDED

#ifdef __OBJC__
@class Cat;
typedef Cat * CatPtr;
#else
typedef void * CatPtr;
#endif

class Person
{
	public:
		Person();

	private:
		CatPtr const cat_;
};

#endif

Let’s first implement Cat. It does not have any methods declared in its interface but we want to override dealloc, just to see whether the Cat objects we create will get deallocated correctly. Note that we are using ARC and thus a call to [super dealloc] is forbidden (and unnecessary).

#import "Cat.h"

@implementation Cat

- (void)dealloc {
	NSLog(@"Meow!");
}

@end

Since we use ARC, the implementation of Person is equally simple:

#import "Cat.h"
#include "Person.hpp"

Person::Person():
	cat_(
		[[Cat alloc] init]
	)
{
}

As I already pointed out in the old article, this can be problematic when Cat * and void * fail to be layout compatible. I still haven’t found any reference to whether this is the case or not; most of the time everything seems to work and you can even hear Apple engineers talk about casting Objective-C object pointer types to void * (if only to advise against it) in some WWDC talks. However, ARC definitively demonstrates that this approach is not the way to go.

To see this, you will first have to compile the above code into a library with ARC enabled. To enable ARC in clang on the command line, for example, you can use the -fobjc-arc option. Next you will need a test program to see the effect. Like this:

#include "Person.hpp"

int
main()
{
	Person p;
}

Its only purpose is to create an instance of our Person class which can then — when going out of scope — be destroyed again. If you save the above program with extension .mm, compile it with ARC support and run it, you will see “Meow!” printed to your terminal. ARC worked it’s magic, obviously, calling our dealloc method in Cat without us ever having to invoke release.

Since you cannot put objects managed by ARC into C structs, this might come as a surprise. In Objective-C++, ARC continues to work when the managed objects are members of a C++ class. The ARC specification explicitly states what happens in section 4.3.5 (as of 2012/09/22):

This restriction [of not being able to have ARC-managed objects inside of structs] does not apply in Objective-C++. However, nontrivally ownership-qualified types are considered non-POD: in C++11 terms, they are not trivially default constructible, copy constructible, move constructible, copy assignable, move assignable, or destructible. It is a violation of C++’s One Definition Rule to use a class outside of ARC that, under ARC, would have a nontrivially ownership-qualified member.

What this means (or what I read from it, that is) is this: ARC basically works by inserting additional code into your files, specifically calls to retain, release and similar reference counting related methods. In Objective-C++ it seems to also generate destructors (among other things) to put these invocations into, if there is no explicitly defined one yet. To any Objective-C++ compiler with ARC enabled (as the one that compiled our library), our Person class now seems to have a non-trivial destructor. However, a simple C++ does not know about ARC and consequently does not see this destructor. This is what that cryptic last sentence of the above quote means and its effect can be seen when you rename our example program to have the extension .cpp and compile it with a C++ compiler. Nothing should be printed if you run the new executable. In violation to the One Definition Rule, our program works with another version of the Person class than our library does.

So what’s the moral? You probably should not use the approach I advertised in my previous article. As I already stated there, the Pimpl pattern is a safe alternative. This article is not supposed to be a Pimpl tutorial but I ported my old example code to using it just to give you an idea. Another way around this problem is to just avoid using ARC in situations like this. However, we still don’t know what other problems possible layout inompatibility can yield, so I would highly recommend not to do this.

Open a Random Page in VoodooPad

I recently fell in love with a little tool called VoodooPad by Flying Meat. VoodooPad is a personal wiki or desktop wiki; the perfect place to develop and structure ideas. However, as far as I know, it misses an essential feature for any wiki system: the random page button!

Luckily, VoodooPad is very scriptable, so I came up with this JSTalk plugin. Installation instructions are included in the README for those of you who are new to scripting VoodooPad.

I also created a Downloads page where you will permanently find a pointer to this and, in the future, other stuff I publish here and elsewhere.

For those interested in the programming aspect of the plugin: Yes, it seems rather long and convoluted for such a small purpose, I’ll admit that, but hear my excuses:

  1. Javascript actually allows for some pretty succinct code, but unfortunately using a bridge like JSTalk/JSCocoa destroys this possibility in this case mainly due to the mixing of object systems and “primitive” types like strings. Bridges generally suck because of this and other things, but we knew that already.
  2. I am a perfectionist and wanted to make every page equally probable. You can draw up a list of pages and pick a random one in one line of code, but VoodooPad includes aliases in this list, making those pages with lots of aliases pointing to them more likely to appear with this approach. I then used this “opportunity” to also filter out the files you might have stored in your VoodooPad document.
  3. This is actually the first “significant” block of JavaScript code I ever wrote.

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

Note: I added a footnote. Go read it.

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.

#import <AppKit/AppKit.h>

int main(int argc, char *argv[])
{
	NSSpeechSynthesizer * synth = [[NSSpeechSynthesizer alloc] init];
	for (int word = 1; word < argc; ++word)
	{
		[synth startSpeakingString:
			[NSString stringWithUTF8String:argv[word]]
		];
		while ([synth isSpeaking]);
	}
}

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):

#ifndef YOUR_FRIENDS_LIBRARY_SPEECH_SPEAKER_HPP
#define YOUR_FRIENDS_LIBRARY_SPEECH_SPEAKER_HPP

#import <AppKit/AppKit.h>
#include <string>

namespace your
{
namespace friends
{
namespace library
{
namespace speech
{

class speaker
{
public:
	speaker();
	~speaker();

	void
	speak(
		std::string const &
	);

	bool
	is_speaking() const;

private:
	NSSpeechSynthesizer * const synth_;
};

}
}
}
}

#endif

with the following implementation in speaker.mm (.mm to let the compiler know that this is an “Objective-C++” file):

#include <speaker.hpp>

your::friends::library::speech::speaker::speaker():
	synth_(
		[[NSSpeechSynthesizer alloc] init]
	)
{
}

your::friends::library::speech::speaker::~speaker()
{
	[synth_ release];
}

void
your::friends::library::speech::speaker::speak(
	std::string const & string
)
{
	[synth_ startSpeakingString:
		[NSString stringWithUTF8String:
			string.c_str()
		]
	];
}

bool
your::friends::library::speech::speaker::is_speaking() const
{
	return [synth_ isSpeaking];
}

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:

/Users/julian/Documents/Blog/objcpp/speaker.mm: In destructor ‘your::friends::library::speech::speaker::~speaker()’:
/Users/julian/Documents/Blog/objcpp/speaker.mm:13: warning: invalid receiver type ‘void *’
/Users/julian/Documents/Blog/objcpp/speaker.mm: In member function ‘void your::friends::library::speech::speaker::speak(const std::string&)’:
/Users/julian/Documents/Blog/objcpp/speaker.mm:25: warning: invalid receiver type ‘void *’
/Users/julian/Documents/Blog/objcpp/speaker.mm: In member function ‘bool your::friends::library::speech::speaker::is_speaking() const’:
/Users/julian/Documents/Blog/objcpp/speaker.mm:31: warning: invalid receiver type ‘void *’

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:

#ifndef YOUR_FRIENDS_LIBRARY_SPEECH_SYNTHESIZER_HPP
#define YOUR_FRIENDS_LIBRARY_SPEECH_SYNTHESIZER_HPP

#ifdef __OBJC__
#import <AppKit/AppKit.h>
#endif

namespace your
{
namespace friends
{
namespace library
{
namespace speech
{

#ifdef __OBJC__
typedef NSSpeechSynthesizer synthesizer;
#else
typedef void synthesizer;
#endif

}
}
}
}

#endif

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.