Writing an Objective-C wrapper for C++

Guides | Tutorial By 3 years ago

Recently at work I completed a C++ library that we’re using in both our iOS and Android apps.

To make it easy for our native iOS app to use, I also wrote an Objective-C wrapper that lets the other parts of the app interface with just Objective-C code. The wrapper’s job is basically to bridge properties and setters, and also handle the C++ memory management.

Here’s the C++ class I will use as an example. It’s pretty basic, and just consists of a constructor, a setter and a getter.

#ifndef CPP_CLASS_H
#define CPP_CLASS_H

#include <cstring>
#include <stdlib.h>

class CppClass
{
public:
	CppClass(const char* string = NULL) { set_string(string); }
	~CppClass() { set_string(NULL); }
	
	void set_string(const char* string)
	{
		if(string_)
			free(string_);
		string_ = (char*)(string ? strdup(string) : NULL);
	}
	
	const char* string() const { return string_; }
	
private:
	char* string_;
};

#endif /* CPP_CLASS_H */

Objective-C is C based and not C++ based. If you try and use C++ code with Objective-C you will get compiler problems. Instead we need to be writing the implementation of the wrapper in Objective-C++. The Objective-C file extension is .m, to use Objective-C++ all you need to do is change the file extension to .mm

In your Objective-C class header for the wrapper you’ll want to not include any C++ classes or code if possible. If you do then any other class that import it will also need to have the .mm extension. You can get around that with some preprocessor hackery, but there’s rarely a case where you need to put your variables in the header anyway.

So for the Objective-C header I’m going to keep things extremely simple and include just an initializer and property. Based on the following, the other classes have no idea that there’s actually C++ backing this class.

ObjcClass.h

@interface ObjcClass : NSObject

@property (nonatomic, copy) NSString* string;

- (instancetype) initWithString:(NSString*) string;

@end

For the implementation, first remember to set .mm as your file extension. You will need to store a pointer to your instantiated C++ class. Instead of doing it in the header, we can do it in an empty category.

@interface ObjcClass ()

@property (nonatomic, readonly) CppClass* internal;

@end

My initWithString: method just allocates a CppClass into the internal variable with whatever string has been given to the method.

- (instancetype) initWithString:(NSString*) string
{
	self = [super init];
	
	if(self != nil)
	{
		_internal = new CppClass(string.UTF8String);
	}
	
	return self;
}

To make things as “Objective-C” as possible, I am converting between Objetive-C and C++ types. The C++ class uses a const char* to store the string, but to make things as native as possible in the initializer I convert it from an NSString to the const char*.

If I had other properties and types I would do the same thing. Even wrapping C++ enums and structs, so only my Objetive-C code is exposed in the public interface.

In my getter and setter for the string property, I will do the same conversions.

- (NSString*) string
{
	const char* string = self.internal->string();
	return string ? @(string) : nil;
}

- (void) setString:(NSString*) string
{
	self.internal->set_string(string.UTF8String);
}

Finally, this is C++ so we need to do some manual memory management. Whenever we’re done with a C++ allocated object, it needs to be deleted. This belongs in the dealloc method.

- (void) dealloc
{
	delete _internal;
}

The entire class:

ObjcClass.mm

#import "CppClass.h"

@interface ObjcClass ()

@property (nonatomic, readonly) CppClass* internal;

@end

@implementation ObjcClass

- (instancetype) init
{
	return [self initWithString:nil];
}

- (instancetype) initWithString:(NSString*) string
{
	self = [super init];
	
	if(self != nil)
	{
		_internal = new CppClass(string.UTF8String);
	}
	
	return self;
}

#pragma mark -
#pragma mark Self

- (NSString*) string
{
	const char* string = self.internal->string();
	return string ? @(string) : nil;
}

- (void) setString:(NSString*) string
{
	self.internal->set_string(string.UTF8String);
}

#pragma mark -
#pragma mark Cleanup

- (void) dealloc
{
	delete _internal;
}

@end

The class I’ve given as an example is obviously extremely simple. There are a lot more challenges to tackle and other things to consider when wrapping larger libraries. Some include:

  • Returning other classes that also need an Objective-C wrapper
  • Using smart pointers so even C++ memory management is automatically handled
  • Calling the same method twice and returning the same object (if you call object.string above you’ll get a different pointer every time even if the value has not changed)
  • TV

    Hi Tom!
    Thank you for the tutorial, it’s very simple and useful.
    I’m currently doing the same thing with a c++ library for a iOS app I’m developing and I have a question regarding the import of the #import “CppClass.h” in the .mm file.
    Imagine that your cpp class has another c++ file imported or some c++ header like iostream.
    That won’t be compiled in Xcode what wound you do in that case?

    Thanks

    • Tom

      Hi there
      It sounds like it should be fine, can you upload an example project where you’re having issues?