Mixing C++ and Objective-C code using XCode


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
// hello_proxy.h:
#pragma once
#include "hello.h"

@interface hello_proxy : NSObject {
    Hello * hello;
}
-(hello_proxy *)init;
-(void)set_hello_world: (NSString *) hello_msg;
-(NSString *)get_hello_world;
@end
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

, , , , , ,

  1. No comments yet.
(will not be published)


Technical Procedures is Digg proof thanks to caching by WP Super Cache