WARNING: Please read before continuing ▼
Difficulty: Expert
Time: varies
Last Updated: 20th October 2009
Applies to: Objective-C
With the release of the iPhone SDK and the growing popularity of OS X, Objective-C is becoming the language of choice for a significant amount of projects.
One inescapable fact in the programming world is the existence of legacy code. Moving to a new language typically means porting or rewriting existing code entirely in the new language. In the case of Objective-C, most existing C and C++ code can be used directly.
While the process is relatively simple, documentation on mixing C/++ and Objective-C is scant; Apple’s own documentation on the subject doesn’t even compile unmodified on XCode 3.2
Lets take this simple C++ program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // main.cpp: #include #include #include "hello.h" using namespace std; int main (int argc, char * const argv[]) { Hello *hello = new Hello; hello->set_hello_world("Hello, World!"); cout << hello->get_hello_world(); return 0; } |
It simply creates an instance of the class “Hello”, sets a value inside that instance, then retrieves that value and outputs it.
1 2 3 4 5 6 7 8 | // Hello.h: #pragma once #include #include "hello.cpp" using namespace std; class Hello; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Hello.cpp: #pragma once #include "hello.h" #include using namespace std; class Hello { private: string hello_world; public: string get_hello_world() { return hello_world; } void set_hello_world(string hello_msg) { hello_world = hello_msg; } }; |
Now, lets assume we want to re-write this in Objective-C. The first step, after setting up XCode for an Objective C project, is to change the file extension of “main.m” to “.mm“.
This instructs the compiler to compile for Objective-C with C++ extensions enabled. All other Objective-C files must also use the “.mm” extension, otherwise you will get all sorts of weird and wonderful compilation errors.
From here, there are 2 ways to approach using C++ objects. You can either call them directly in amongst your Objective-C code, or you can write a proxy.
The former is very quick, and would be suitable for trivial classes or structs. The latter, allows your main Objective-C code to be a lot more readable, and allows you to hide away things such as string type conversions (NSString* to std::string and back) in the proxy.
To just mix C++ and Objective-C calls, main.mm would look like this:
1 2 3 4 5 6 7 8 | int main (int argc, const char * argv[]) { Hello *hello = new Hello; hello->set_hello_world("Hello, World!"); NSLog(@"%s", string(hello->get_hello_world()).c_str()); return 0; } |
As you can see, the class is instantiated in exaxctly the same way as the previous example. Before being output by NSLog, the string has to be converted to a format that NSLog understands; either NSString or a cstring.
This process works perfectly well for small, trivial projects, but for anything substantial the code will quickly degenerate into a mess with type conversions all over the place.
Writing a proxy class will allow you to hide away all of the details, and provide a neat interface to anyone using the class.
Our Objective-C proxy for hello.h/hello.cpp would look something like this:
1 2 3 4 5 6 7 8 9 10 11 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // hello_proxy.mm: #pragma once #import "hello_proxy.h" #import "hello.h" @implementation hello_proxy -(hello_proxy *)init { self = [super init]; if (self) { hello = new Hello; } return self; } -(void)set_hello_world:(NSString *)hello_msg { hello->set_hello_world([hello_msg cStringUsingEncoding:NSASCIIStringEncoding]); } -(NSString *)get_hello_world { return [[NSString alloc] initWithCString:hello->get_hello_world().c_str() encoding:NSASCIIStringEncoding]; } @end |
As you can see, we create a method in the proxy class that is equivalent to each method in the C++ Hello class, including a constructor even if the original C++ does not have one (we need to instatiate an instance of the C++ class, and the constructor is the obvious place to do it. Converting C++ std::string into Objective-C NSString* is handled within the proxy.
The result:
1 2 3 4 5 6 7 8 9 10 11 12 | // Hello, World!.mm #import "hello_proxy.h" #import "hello.h" int main (int argc, const char * argv[]) { hello_proxy *hello = [[hello_proxy alloc] init]; [hello set_hello_world:@"Hello, World!"]; NSLog(@"%s", [hello get_hello_world]); return 0; } |
A much neater, more readable piece of pure Objective-C.
Code:
C++ Hello, World
Objective-C Proxied Hello, World
Objective-C Simple Hello, World