Created
April 8, 2010 21:58
-
-
Save nevyn/360606 to your computer and use it in GitHub Desktop.
Transparent ObjC<>C++ bridge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Say you have a largeish C++ class. You want to use it from ObjC. ObjC++ is painful; | |
C++ is painful. .mm files are bad. How about if we could just treat that C++ | |
object as an ObjC object whenever it exits C++ land? | |
Apple already solved this problem once with toll free bridging. The tricky part is | |
vtables -- with a vtable, isa isn't at offset 0 of the memory layout of the object. | |
The code below is a work in progress to work around this. | |
*/ | |
#import <Foundation/Foundation.h> | |
/////// ObjcObject.h /////////// | |
#include <objc/runtime.h> | |
class ObjcObject { | |
Class isa; | |
mutable size_t isa_offset; | |
public: | |
template<typename CPP> static size_t objcHeaderOffset(CPP* cpp) { | |
return (size_t)((char *)&((CPP*)cpp)->isa - (char*)cpp); | |
} | |
template<typename CPP, typename OBJC> static OBJC *objc(CPP *cpp) { | |
if(!cpp) return nil; | |
if(cpp->isa_offset == -1) | |
cpp->isa_offset = objcHeaderOffset<CPP>(cpp); | |
OBJC* o = (OBJC*)((char*)cpp + cpp->isa_offset); | |
return (OBJC*)o; | |
} | |
template<typename CPP, typename OBJC> static CPP *cpp(OBJC *objc) { | |
if(!objc) return NULL; | |
size_t offset = *(size_t*)((char*)objc + sizeof(Class)); | |
return (CPP*)((char*)objc - offset); | |
} | |
protected: | |
ObjcObject(const char *objcname) : isa_offset(-1) { | |
isa = (Class)objc_getClass(objcname); | |
} | |
}; | |
/////// Base /////////// | |
class Base { | |
public: | |
virtual int getB() = 0; | |
virtual void setB(int _) = 0; | |
}; | |
/////// Foo /////////// | |
class Foo : public ObjcObject, public Base { | |
int a; | |
int b; | |
public: | |
Foo() : a(0), b(0), ObjcObject("ObjcFoo") {} | |
int getA() { return a; } | |
void setA(int _) { a = _; } | |
virtual int getB() { return b; } | |
virtual void setB(int _) { b = _; } | |
}; | |
/////// ObjcFoo /////////// | |
@interface ObjcFoo : NSObject | |
@property (readonly) Foo *foo; | |
@end | |
@implementation ObjcFoo | |
-(Foo*)foo; { return ObjcObject::cpp<Foo, ObjcFoo>(self); } | |
-(int)a; | |
{ | |
return self.foo->getA(); | |
} | |
-(void)setA:(int)_; | |
{ | |
self.foo->setA(_); | |
} | |
-(int)b; | |
{ | |
return self.foo->getB(); | |
} | |
-(void)setB:(int)_; | |
{ | |
self.foo->setB(_); | |
} | |
@end | |
/////// main.mm /////////// | |
int main (int argc, const char * argv[]) { | |
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | |
Foo *foo = new Foo(); | |
foo->setA(3); | |
foo->setB(6); | |
ObjcFoo *ofoo = ObjcObject::objc<Foo, ObjcFoo>(foo); | |
// insert code here... | |
NSLog(@"Hello, World! %d %d", [ofoo a], [ofoo b]); | |
[pool drain]; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment