Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C - Disadvantages to Bridging With C++?

Tags:

So, I was bored today, and decide to mess with C++/Obj-C interpolation, and I found a way to create a very interesting setup.

@protocol NSCPPObj <NSObject>  -(id) init; -(id) initWithInt:(int) value; -(int) somethingThatReturnsAValue; -(void) doSomething;  @end  class NSCPPObj : objc_object { public:         static Class cls();      int iVar;      NSCPPObj();     NSCPPObj(int);      int somethingThatReturnsAValue();     void doSomething(); }; 

As you can see, the interface is quite straightforward, and easy to understand. We create two (almost) identical interfaces, one for a C++ object, and another for a Obj-C protocol.

Now, I found a way to implement this, but brace yourself, this gets ugly:

// NSCPPObj.mm #import <objc/runtime.h> #import <iostream>  #import "NSCPPObject.h"  Class NSCPPObj_class = nil;  __attribute__((constructor)) static void initialize() {     NSCPPObj_class = objc_allocateClassPair([NSObject class], "NSCPPObj", 0);      class_addMethod(NSCPPObj_class->isa, @selector(alloc), imp_implementationWithBlock(^(id self) {         return class_createInstance(NSCPPObj_class, sizeof(struct NSCPPObj));     }), "@@:");      class_addMethod(NSCPPObj_class, @selector(init), imp_implementationWithBlock(^(id self) {         return self;             }), "@@:");      class_addMethod(NSCPPObj_class, @selector(initWithInt:), imp_implementationWithBlock(^(id self, int value) {         ((struct NSCPPObj *) self)->iVar = value;          return self;     }), "@@:i");      class_addMethod(NSCPPObj_class, @selector(doSomething), imp_implementationWithBlock(^(id self) {         ((struct NSCPPObj *) self)->doSomething();     }), "v@:");     class_addMethod(NSCPPObj_class, @selector(somethingThatReturnsAValue), imp_implementationWithBlock(^(id self) {         return ((struct NSCPPObj *) self)->somethingThatReturnsAValue();     }), "i@:");      objc_registerClassPair(NSCPPObj_class); }  Class NSCPPObj::cls() {     return NSCPPObj_class; }  NSCPPObj::NSCPPObj() {     this->isa = NSCPPObj_class;     [((id<NSCPPObj>) this) init]; }  NSCPPObj::NSCPPObj(int value) {     this->isa = NSCPPObj_class;     [((id<NSCPPObj>) this) initWithInt:value]; }  void NSCPPObj::doSomething() {     std::cout << "Value Is: " << [((id<NSCPPObj>) this) somethingThatReturnsAValue] << std::endl; }  int NSCPPObj::somethingThatReturnsAValue() {     return iVar; } 

I'll summarize what this does:

  1. Allocates a Class Pair
  2. Adds all class and instance methods to the object
  3. Registers the class Pair

Now, as you can see, this isn't very flexible, but it does work, and it's a two-way street:

id<NSCPPObj> obj = [[NSCPPObj::cls() alloc] initWithInt:15]; [obj doSomething];  NSLog(@"%i", [obj somethingThatReturnsAValue]); NSLog(@"%@", obj);  NSCPPObj *objAsCPP = (__bridge NSCPPObj *) obj;  objAsCPP->doSomething(); std::cout << objAsCPP->somethingThatReturnsAValue() << std::endl; 

You can also create the object by using new NSCPPObj(15), but remember to delete it! Obviously, this can work in a ARC or non-ARC environment, but ARC requires a few extra bridged casts.

So, I come to the real question:
What are the pros/cons of this design structure? I can list a few off of the top of my head:

Pros:

  1. Operator Overloading with C++
  2. Dynamic method binding with ObjC
  3. Can be constructed in either a C++ or ObjC fashion

Cons:

  1. Hard-to-read implementation
  2. Selectors & bindings must be added for every C++ implementation added to the interface
  3. Class object cannot be referenced directly

So, after all that, would you recommend this design structure in an application? and why.

like image 201
Richard J. Ross III Avatar asked Apr 04 '12 15:04

Richard J. Ross III


People also ask

Is Objective-C compatible with C?

Objective-C is an object-oriented programming language that is a superset of C, as the name of the language might reveal. This means that any valid C program will compile with an Objective-C compiler. It derives all its non-object oriented syntax from C and its object oriented syntax from SmallTalk.

Which is faster C or Objective-C?

Calling a C function is about 3-6 times faster than calling an Obj-C method, so how can they be equally fast? They can only be equally fast if you never call methods in Obj-C but that some defies the purpose of using Obj-C.


1 Answers

So, after all that, would you recommend this design structure in an application? and why.

No.

It is a really nice bit of code; I particularly like the use of imp_implementationWithBlock() (but I admit I might be partial to that particular feature of the runtime ;). And, of course, explorations like this are always an incredibly valuable learning tool.

The issue, in the context of "real world paying project" use, is that you are effectively creating a relatively generic bridge that will then have to have specific bridges at either end to interface with either typical C++ libraries or typical Objective-C APIs/libraries. To put it another way, you have effectively created a new runtime derived from an amalgamation of two existing runtimes.

And, as you point out in the Cons, you pretty much have to touch, wrap, modify and/or debug a shim on top of every C++ class you want to bring into this pattern.

In working with quite a bit of Objective-C++ code over the last 20+ years, a bridge like this is generally more trouble than it is worth. You would likely be better off -- spend less time writing and debugging code -- creating simple Objective-C wrappers around the C++ (or C, frankly) APIs that can then be integrated with and consumed by the targeted system's Objective-C frameworks.

like image 124
bbum Avatar answered Oct 12 '22 23:10

bbum