iPhoneOS: using detachNewThreadSelector method inside a C++ class method - c++

I have a C++ class method where i need to call the "detachNewThreadSelector" method with all the parameters.
Here lies the problem, as my class is not objective C i don't have a self pointer. Also i don't see how i will be able to call a class method from the method that i will set as selector.
Please do ask if my question is not clear, i am not from a english speaking country.
Here is some code.
ALuint AudioController::PlayStream(const string& asset)
{
//attach to a thread
[NSThread detachNewThreadSelector:(SEL)selector toTarget:(id)selfwithObject:(id)argument]
}
void AudioController::RotateThread(const string& soundKey)
{
}
As you can see how do i pass the RotateThread method as a selector to the "detachNewThreadSelector" and also where do i get the self pointer.
Any help much appreciated.
Thanks

You can't do this. It isn't as a simple as "Where do I get the self pointer?" The actual question is, "Where do I get something that can respond to messages?" Because a C++ class can't.
Objective-C classes, objects and methods are completely different things from C++ classes, objects and methods. The fact that the two languages use the same terminology and use the things for similar purposes confuses a lot of people, but to be clear: They are totally different things that work in very different ways in the two languages. Case in point: C++ methods are simply called rather than dispatched based on a selector like Objective-C methods. And C++ classes aren't even objects.
You have two real options here:
Create an Objective-C class that has the behavior you want.
Use a C++ concurrency solution.

you may not use c++ object in this manner (as an argument to this NSThread method). if your case is simple (read: few interfaces declared), then you can create a utility (objc) class to handle the message, and to then pass the argument back to the AudioController instance. illustration:
(non-compiled pseudo code follows)
namespace pseudo_object {
template <typename> class reference_counted;
}
#interface MONAudioControllerWorker : NSObject
{
pseudo_object::reference_counted<AudioController> audioController_;
std::string asset_;
}
+ (MONAudioControllerWorker *)newMONAudioControllerWorkerWithAudioController:(pseudo_object::reference_counted<AudioController>&)audioController asset:(const std::string&)asset;
- (void)secondaryWorker;
#end
#implementation MONAudioControllerWorker
+ (MONAudioControllerWorker *)newMONAudioControllerWorkerWithAudioController:(pseudo_object::reference_counted<AudioController>&)audioController asset:(const std::string&)asset
{
/* ... */
}
- (void)secondaryWorker
{
NSAutoreleasePool * pool([NSAutoreleasePool new]);
audioController_->RotateThread(asset_);
[pool release];
}
#end
/* static */
ALuint AudioController::PlayStream(pseudo_object::reference_counted<AudioController>& This, const string& asset)
{
/* attach to a thread */
MONAudioControllerWorker * controller = [MONAudioControllerWorker newMONAudioControllerWorkerWithAudioController:This asset:asset];
[NSThread detachNewThreadSelector:#selector(secondaryWorker) toTarget:controller withObject:0];
[controller release];
}
sometimes it is just easier to create an objc class which may contain a simplified (generic) interface for this purpose (i.e. reusable beyond this object), or to use more traditional threading routines (pthreads). if this is the only case in the project, then it should be fine. otherwise, you end up with many utility classes/symbols and much more to maintain. illustration:
#interface MONAudioControllerWrapper : NSObject
{
AudioController audioController_;
std::string asset_;
}
+ (MONAudioControllerWrapper *)newMONAudioControllerWrapperWithAsset:(const std::string&)asset;
- (void)playStream;
#end
#implementation MONAudioControllerWrapper
+ (MONAudioControllerWrapper *)newMONAudioControllerWrapperWithAsset:(const std::string&)asset
{
/* ... */
}
- (void)secondaryWorker
{
NSAutoreleasePool * pool([NSAutoreleasePool new]);
audioController_->RotateThread(asset_);
[pool release];
}
- (void)playStream
{
[NSThread detachNewThreadSelector:#selector(secondaryWorker) toTarget:self withObject:0];
}
#end

As others have said, you can't use detachThreadWithSelector: passing a C++ object as a target or using a C++ method as the selector.
You have two strategies:
wrap the object and selector with an OBjective-C object and selector e.g.
myAudioControllerWrapper = [[OCAudioControllerWrapper alloc] initWithRealController: this];
// need some code to release once thread is complete
[NSThread detachNewThreadSelector: #selector(wrapperSelector:) target: myAudioControllerWrapper withObject: soundKeyAsNSObject];
and your wrapper selector looks like:
-(void) wrapperSelector: (id) key
{
cppController->rotateThread([key cppObject]);
}
Use some other thread mechanism more in keeping with the C++ paradigm. Grand Central Dispatch might be the one if your platform supports it.

Related

C++ object as an Objective-C++ property

I want to declare a C++ property inside my Objective-C class.
What kind of attributes should I set it to? It seems that strong or retain will lead to an error saying that it is not an object.
How could I manage its memory properly?
You are right, the property cannot be weak, strong, or retained; for that it would have to be a pointer to an Objective-C object. If you don't use any attributes on a C++ property, it will default to unsafe_unretained,assign,atomic.
A few other things to consider, assuming the Objective-C(++) object controls the lifetime of the C++ property:
Because you can't do much with a C++ object in Objective-C, the
property is mostly useful in Objective-C++ code, where you can mix
Objective-C and C++.
Because you have to manage the property's memory on your own, you
need a custom setter.
Because the property defaults to atomic, you need to use
synchronization in the setter and need a custom getter, too. You
could declare it nonatomic, in which case you would not need to
synchronize and would not need a custom getter.
You can implement dealloc to ensure the C++ object is freed when
the Objective-C++ object goes away.
Here's some useful documentation from Apple: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html.
And here is a quick example. Let's say the C++ class you are using for the property is called MyCPP. In a header you could have:
#interface ClassOCPP : NSObject
// This property can only be used in Objective-C++ code
#ifdef __cplusplus
#property /*(unsafe_unretained,assign,atomic)*/ MyCPP * myCPP;
#endif
// Other stuff may be usable in regular Objective-C code.
#end
Implementation could be as follows (in a .mm file; remember, it's Objective-C++):
#implementation ClassOCPP
{
MyCPP * _myCPP;
}
-(id)init {
self = [super init];
_myCPP = NULL;
return self;
}
-(void)setMyCPP:(MyCPP*)newVal {
#synchronized(self) {
delete _myCPP; // it's OK to delete a NULL pointer
_myCPP = newVal;
}
}
-(MyCPP*)myCPP {
return _myCPP;
}
-(void)dealloc {
puts("De-allocating ClassOCPP.");
delete _myCPP;
}
#end
IIRC, you need to manage it like any other C++ object, so just use assign as the attribute, and you should be good.

Leaking C++ shared_ptr in Objective-C Block

Summary:
In the example application below, a shared_ptr is being captured in an Objective-C block. The Objective-C block is being assigned to an ivar of a dynamically created class using the Objective-C runtime API object_setIvarWithStrongDefault. When the Objective-C object is deallocated, the shared_ptr is leaking and the C++ object that it is retaining is not deleted. Why is that?
When object_setIvar is used instead, then the leak is prevented but the ivar points to garbage once the block goes out of scope as object_setIvar assumes an assignment of unsafe_unretained.
I assume this has to do with how Objective-C captures C++ objects, copies blocks and how shared_ptr handles being copied, but I was hoping someone could shed some light on this more than the documentation listed below.
References:
Apple's Blocks and Variables Documentation contains a brief section on C++ objects but it's not entirely clear to me how it affects shared pointers.
LLVM's Documentation on Blocks & C++ Support is a bit more detailed than Apple's...
objc-class.mm contains the implementation for object_setIvarWithStrongDefault
Backstory:
This sample code is extracted from a much larger project and has been significantly reduced to the minimum required to show the issue. The project is an Objective-C macOS application. The application contains several monolithic C++ objects that are glorified key/value stores. Each object is an instance of the same class, but templated on the key type. I want to dynamically create an Objective-C class that contains typed property getters which are backed by the C++ class.
(Yes, this could all be done manually by just writing lots-and-lots of getters myself, but I'd prefer not to. The C++ class has enough information to know the names of the properties and their types, thus I'd like to use some meta-programming techniques to "solve" this.)
Notes:
In an ideal world, I'd just be able to define an iVar on an Objective-C class of the appropriate shared_ptr type but I can't figure out how to do that using the Objective-C runtime APIs.
Given this:
std::shared_ptr<BackingStore<T>> backingStore
How do you use this:
class_addIvar and object_setIvar
Since I couldn't figure that out, I decided to just wrap the shared_ptr into an Objective-C block since blocks are first-class objects and can be passed around where an id is expected.
Sample Application:
(Copy/paste into something like CodeRunner to see output)
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <memory>
typedef NSString* (^stringBlock)();
/**
* StoreBridge
*
* Objective-C class that exposes Objective-C properties
* which are "backed" by a C++ object (Store). The implementations
* for each property on this class are dynamically added.
*/
#interface StoreBridge : NSObject
#property(nonatomic, strong, readonly) NSString *storeName;
#end
#implementation StoreBridge
#dynamic storeName;
- (void)dealloc {
NSLog(#"StoreBridge DEALLOC");
}
#end
/**
* BackingStore
*
* C++ class that for this example just exposes a single,
* hard-coded getter function. In reality this class is
* much larger.
*/
class BackingStore {
public:
BackingStore() {
NSLog(#"BackingStore constructor.");
}
~BackingStore() {
NSLog(#"BackingStore destructor.");
}
NSString *name() const {
return #"Amazon";
}
// Given a shared_ptr to a BackingStore instance, this method
// will dynamically create a new Objective-C class. The new
// class will contain Objective-C properties that are backed
// by the given BackingStore.
//
// Much of this code is hard-coded for this example. In reality,
// a much larger number of properties are dynamically created
// with different return types and a new class pair is
// only created if necessary.
static id makeBridge(std::shared_ptr<BackingStore> storePtr) {
// For this example, just create a new class pair each time.
NSString *klassName = NSUUID.UUID.UUIDString;
Class klass = objc_allocateClassPair(StoreBridge.class, klassName.UTF8String, 0);
// For this example, use hard-coded values and a single iVar definition. The
// iVar will store an Objective-C block as an 'id'.
size_t ivarSize = sizeof(id);
NSString *ivarName = #"_storeNameIvar";
NSString *encoding = [NSString stringWithFormat:#"%s#", #encode(id)];
SEL selector = #selector(storeName);
// Implementation for #property.storeName on StoreBridge. This
// implementation will read the block stored in the instances
// iVar named "_storeNameIvar" and call it. Fixed casting to
// type 'stringBlock' is used for this example only.
IMP implementation = imp_implementationWithBlock((id) ^id(id _self) {
Ivar iv = class_getInstanceVariable([_self class], ivarName.UTF8String);
id obj = object_getIvar(_self, iv);
return ((stringBlock)obj)();
});
// Add iVar definition and property implementation to newly created class pair.
class_addIvar(klass, ivarName.UTF8String, ivarSize, rint(log2(ivarSize)), #encode(id));
class_addMethod(klass, selector, implementation, encoding.UTF8String);
objc_registerClassPair(klass);
// Create instance of the newly defined class.
id bridge = [[klass alloc] init];
// Capture storePtr in an Objective-C block. This is the block that
// will be stored in the instance's iVar. Each bridge instance has
// its own backingStore, therefore the storePtr must be set on the
// instance's iVar and not captured in the implementation above.
id block = ^NSString* { return storePtr->name(); };
Ivar iva = class_getInstanceVariable(klass, ivarName.UTF8String);
// Assign block to previously declared iVar. When the strongDefault
// method is used, the shared_ptr will leak and the BackingStore
// will never get deallocated. When object_setIvar() is used,
// the BackingStore will get deallocated but crashes at
// runtime as 'block' is not retained anywhere.
//
// The documentation for object_setIvar() says that if 'strong'
// or 'weak' is not used, then 'unretained' is used. It might
// "work" in this example, but in a larger program it crashes
// as 'block' goes out of scope.
#define USE_STRONG_SETTER 1
#if USE_STRONG_SETTER
object_setIvarWithStrongDefault(bridge, iva, block);
#else
object_setIvar(bridge, iva, block);
#endif
return bridge;
}
};
int main(int argc, char *argv[]) {
#autoreleasepool {
std::shared_ptr<BackingStore> storePtr = std::make_shared<BackingStore>();
StoreBridge *bridge = BackingStore::makeBridge(storePtr);
NSLog(#"bridge.storeName: %#", bridge.storeName);
// When USE_STRONG_SETTER is 1, output is:
//
// > BackingStore constructor.
// > bridge.storeName: Amazon
// > StoreBridge DEALLOC
// When USE_STRONG_SETTER is 0, output is:
//
// > BackingStore constructor.
// > bridge.storeName: Amazon
// > BackingStore destructor.
// > StoreBridge DEALLOC
}
}
Let's jump in a time machine real quick, C.A. 2010. It's a simpler time, before having to deal with multi-architecture slices, 64 bits, and other fancy things, like importantly ARC.
In this seemingly distant world to today, when you had memory, you had to release it yourself gasp. This meant, that if you had an iVar on your class, you had to explicitly, inside dealloc call release on it.
Well, this doesn't actually change with ARC. The only thing that changes is that the compiler generates all of those nice release calls for you inside of dealloc, even if you don't define the method. How nice.
The problem here, however, is that the compiler doesn't actually know about your iVar containing the block - it's completely defined at runtime. So how could the compiler release the memory?
The answer is it doesn't. You'll need to do some magic to make sure that you release this stuff at run-time. My suggestion would be to iterate over the iVars of the class, and set them to nil, rather than call objc_release directly (as it causes much weeping and gnashing of teeth if you're using ARC).
Something like this:
for (ivar in class) {
if ivar_type == #encode(id) {
objc_setIvar(self, ivar, nil)
}
}
Now, if you ever go in and add an intentionally __unsafe_unretained ivar to this class you'll possibly have more issues. But you really shouldn't be inheriting from classes like this, mmkay?

How to call C++ method from Objective-C Cocoa Interface using Objective-C++

Right now my OS X 10.9 project is set up with a GUI in Objective-C and all the processing in a C++ class. This doesn't seem like the best way, but this was given to me and I have to work with these constraints. I currently have a NSSlider in the Cocoa GUI. I want to be able to have this NSSlider control a variable x in the C++ class.
Currently the tolerance bar is working when I run the program (working meaning it's continuously updating its value as per my sliding).
Header for NSSlider (Slider.h):
#interface Slider : NSObject {
#private
__weak IBOutlet NSTextField *sliderValue;
__weak IBOutlet NSSlider *slider;
}
- (IBAction)updateSliderValue:(id)sender;
#end
Implementation for the NSSlider (Slider.mm):
#import "Slider.h" // The NSSlider object
#import "CMain.h" // The C++ class that does all the processing (not sure if this is the way to include a C++ interface in an Objective-C++ class or if this will allow me to call its methods)
#implementation Slider
-(void) awakeFromNib {
[slider setIntValue:40];
[sliderValue setIntValue:[slider intValue]];
}
- (IBAction)updateSliderValue:(id)sender {
[sliderValue setIntValue:[slider intValue]];
// Here is where I'd think that if I have a setValueofX(int number) method in the C++ class
// I could call this somehow using objective-C++. I don't know if I can or how to call this method here
}
Here is the relevant snippet from Main.h:
class CMain(){
public:
CMain();
void setValueofX(int number);
int getValueofX();
private:
int x;
};
How do I include CMain.h in the Objective-C++ (which, the .h file or .mm file? and how?) such that it would allow me to call the methods in CMain.h? How would I phrase the method call in Objective-C to set x's value using the setValueofX(sliderValue)?
Please let me know if more information is needed! Any help or thoughts are appreciated!
Thanks!
The code for calling the setter is the same as it would be in C++. However, you'd need a pointer to the CMain object to be able to call the method on it. I don't know where that object currently resides. If there is no object yet in another object, you probably want to create one, which you can by just declaring an instance variable CMain myCMain; and then calling myCMain.setValueOfX( [slider intValue] ); in updateSliderValue:.
As to where to include the C++ class, it's really your choice. However, if you use any C++ in your header, it will take a bunch of careful extra work to include it from plain .m files. So in general I try to stick to plain C and ObjC in the header, and only use C++ in the .mm file. You can use a class extension (sometimes called "class continuation" as well) to declare additional ivars in your .mm file to keep them out of the header.
If you want more info about ObjC++, I answered in detail on another post: Can I separate C++ main function and classes from Objective-C and/or C routines at compile and link? and that also links to a Podcast I was a guest on where I talk about a lot of the details and tricks for integrating ObjC and C++.
Aside: I hope these are not actual names and comments you're using. Never have an ObjC class without a prefix (3 letters, Apple stated they reserve all 2-letter prefixes for their own use, and they've used un-prefixed internal class names in the past which broke some peoples' programs). Also, "Slider" is a bad name, as NSSlider is the actual slider, and this sounds like it should be a subclass. You really want to call this an ISTSliderController or whatever.
I found a solution by noting that there was an existing wrapper class CWrapper.h and it was an objective-C++ implementation class CWrapper.mm. There was a CMain variable instantiated as cmain. I made a getter method for x and I simply created a static method in the wrapper class + (void) passX:(int) number; that I called in the slider class. I chose a static method because this for this application, this value will never have to be different between objects. I hope I made the right choice here!
See code changes below:
I added this to the Slider.h file.
- (int)X;
I added the getter and [CWrapper passX:[self getX]]; to the updateSliderValue method in the Slider.mm file.
- (int)X {
return [slider intValue];
}
- (IBAction)updateSliderValue:(id)sender {
[sliderValue setIntValue:[slider intValue]];
[CWrapper passX:[self getX]];
}
This is the code I added to CWrapper.h.
+ (void) passX:(int) number;
This is the code I added to CWrapper.mm.
+ (void) passX:(int)num
{
cmain.setValueofX(num);
}
Here's an all objective-c and objective-c++ answer:
CMain.h:
#ifndef Testing_CMain_h
#define Testing_CMain_h
#interface CCMain : NSObject
-(CCMain*) init;
-(void) dealloc;
-(void)setValueofX:(int)number;
-(int)getValueofX;
#end
#endif
CMain.mm:
#import <Foundation/Foundation.h>
#import "CMain.h"
class CMain {
private:
int x;
public:
CMain() : x(0) {}
void setValueofX(int number) {x = number;}
int getValueofX() const {return x;}
};
#interface CCMain ()
#property (nonatomic, assign) CMain* inst;
#end
#implementation CCMain
-(CCMain*) init
{
if (!_inst)
{
_inst = new CMain();
}
return self;
}
-(void) dealloc
{
delete _inst;
_inst = nil;
}
-(void)setValueofX:(int)number
{
_inst->setValueofX(number);
}
-(int)getValueofX
{
return _inst->getValueofX();
}
#end
and if you want to use C-style functions then:
setValueofX(cmain_instance, value);
int val = getValueofX(cmain_instance);
And this works because a C++ class function in C is the same as:
void CMain::MyFunc(int X);
//vs..
void MyFunc(CMain* inst, int X);
Both are the exact same thing.

Generic Objective-to-C++ translation strategy

I'd like to expose ObjC notification handlers to my C++ client code.
I'm doing it this way.
I wrapped an ObjC object (call it X) inside a C++ object.
X observes the notification and registers a callback (call it F).
F translates the ObjC structures from the notification into their C++ counterparts, calls a user-registered global C++ callback function (call it FF). The translated C++ structs become the input arguments of FF.
Now the problem is that the arguments' original ObjC structures are complex, containing multiple layers of ObjC objects that need to be translated.
On my side, the wrapper observer F needs to do nothing special, just calling the client's FF.
What is the more proper strategy of my translation at this point?
Should I:
Translate those structures down to the bottom-level of all their members so that I have equivalent C++ structures to use as the arguments, or,
Create a C++ class to wrap these arguments into one object and expose the class interface to user so they can use those C++-wrapped arguments in their C++ implementation, or,
Give up on the wrapping idea and ask user to code in ObjC and register their own observer functions directly?
My targeted users are iOS developers that may or may not be Android developers too.
You can mix c++ and objective-c++ in a .mm implementation file. This means you can give a c++ lambda (or block) to the objective-c++ class that references your c++ owner.
something like this:
implementation.mm:
#interface Shim : NSObject
{
std::function<void>() _notify;
}
#end
#implementation Shim
- void register_cpp(std::function<void>() f)
{
_notify = std::move(f);
}
- (void) my_handler()
{
if(_notify)
_notify();
}
#end
struct cpp_class::impl {
impl()
: _shim([Shim alloc[init]])
{
_shim.register_cpp(std::bind(&impl::callback, this));
}
private:
void callback() {
// do callback here;
}
Shim* _shim;
};
cpp_class::cpp_class()
: _impl(new impl)
{
}
cpp_class::~cpp_class()
{
delete _impl;
}
header.h:
struct cpp_class{
cpp_class();
~cpp_class();
private:
struct impl;
impl* _impl;
};
In reality you'll want to be careful to ensure that objects still exist when doing callbacks (argues for weak_ptr::lock(), enable_shared_from_this, etc.) since objective-c likes to put callbacks onto a thread's run loop (basically a queue) and that means your c++ object can go away before the callback arrives - but this code should give you the right idea.

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

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:
Allocates a Class Pair
Adds all class and instance methods to the object
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:
Operator Overloading with C++
Dynamic method binding with ObjC
Can be constructed in either a C++ or ObjC fashion
Cons:
Hard-to-read implementation
Selectors & bindings must be added for every C++ implementation added to the interface
Class object cannot be referenced directly
So, after all that, would you recommend this design structure in an application? and why.
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.