0 Replies - 2157 Views - Last Post: 16 January 2012 - 05:05 PM

#1 Hyper_Eye  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 39
  • View blog
  • Posts: 116
  • Joined: 13-September 07

Implementing C++ interfaces with Objective-C classes

Posted 16 January 2012 - 05:05 PM

I have been dabbling with Objective-C for a while. I have a bit of a love/hate relationship with it so far but once you get over the syntax choices that were made (and I do understand the origins of them) I find it is a pretty interesting language. So now I would like to minimize my exposure to it :lol:. Actually, I was just doing some experimentation. I am finding Objective-C++ offers some interesting possibilities. I have not seen this subject thoroughly explored. I have found things that touch on what the pitfalls of mixing the two might be and where the incompatibilities fall but I have found little in the way of ideas for taking advantage of it to solve real needs.

So what I started thinking about was how I would want to go about designing portable code that takes ObjC into account but is not limited to platforms that support ObjC. Obviously I need to write much of the code in a language other than ObjC if this is the goal. One simple way is to write all the busy code in C and use pre-compiler directives to build with either ObjC or C where the code touches the required interfaces (GUI and such.) I'm not really keen on that idea because I would like to start with an object-oriented design and build an implementation with sensible classes. That leads me to Objective-C++ and I think it is not only a good answer but a powerful one. The reason I say that is I should be able to apply design patterns as needed and apply good object-oriented design to the whole project without worrying about the portability of my classes.

My ultimate goal is to write most of the code in C++ and provide C++ interfaces that can be implemented where a platform implementation is needed and the proper instantiation would be obtained from class factories. This is a fairly common object-oriented design approach. Thinking about a GUI, specifically, the idea is to be able to implement GUI classes to an interface so that the application is GUI implementation neutral. It wouldn't care if the implementation were GTK, Qt, Cocoa, etc. And ultimately it wouldn't care if the implementation is in Objective-C.

The problem then is implementing those C++ interfaces with Objective-C classes. All Objective-C++ is, essentially, is the Objective-C language built with a C++ compatible compiler (g++, etc). Nothing was done to create compatibility between the two class types. C++ classes can contain ObjC elements and ObjC classes can contain C++ elements. What you can't do is extend or implement a C++ class with an Objective-C class and vice versa. You can't cast between the two class types obviously. So I decided the first solution is essentially to use bridges which is what I'm going to demonstrate here. The idea is that I have a C++ interface that is implemented by an Objective-C class. Obviously the ObjC class can't inherit from the interface so it inherits from NSObject or a child of it. The thing that binds the ObjC implementation to the C++ interface is a C++ bridge class that implements the interface. The bridge contains the ObjC implementation which is allocated when the bridge is constructed and destroyed when the bridge is destructed. All calls into the bridge are directed into the ObjC class. This is very simple and quite effective. You can pass the C++ bridge into methods that accept the interface and they are none the wiser.

So I have posted a lot of information just to show something that is really incredibly simple. My main goal here is to get feedback on how this could be improved, if there are other methods that might be better, and what pitfalls could be looming. This example does not consider the handling of exceptions in any way. That is something I have not dug into yet. It does not demonstrate passing C++ references though that should work. I would like to experiment with that more. One of the things I am thinking about is how the bridge could be built with macros so that the developer can simply build the Objective-C interface and issue a macro for the class and each method implemented so that there aren't a bunch of bridge headers hanging about. If anyone has some ideas on that it would be great to see. I am pretty sure I could slap some macros together pretty easily but, again, the exceptions are something to think about.

Anyway, here is an example of what I have done so far. Like I said, this is not complicated and it works very well.

Here is a UML class diagram for the three important players here:

Posted Image

So the first thing I need to code up is that C++ interface and here it is:

//
//  CppInterface.h
//  TestCppInterface
//

#ifndef TestCppInterface_CppInterface_h
#define TestCppInterface_CppInterface_h

class CppInterface
{
public:
    virtual ~CppInterface() {};
    
    virtual void methodA() = 0;
    
    virtual void methodB() = 0;
    
    virtual int value() const = 0;
    
    virtual void setValue(int value) = 0;
};

#endif


Alright. So this is a common C++ interface. Nothing special here.

Next would be implementing the interface with an Objective-C class. Obviously, as already stated, I can't inherit from this interface so I will implement the interface but inherit from NSObject. (A little bit of expanded thought... this could actually be an Objective-C interface itself and the bridge could actually be used for multiple ObjC implementations which could be handled by the class factory.)

Here is the declaration:

//
//  CppInterfaceImpl.h
//  CppInterface
//

#import <Foundation/Foundation.h>

@interface CppInterfaceImpl : NSObject
{
    int value;
}

@property int value;

-(void) methodA;

-(void) methodB;

@end



Here is the definition:

//
//  CppInterfaceImpl.m
//  CppInterface
//


#import <Foundation/Foundation.h>

#import "CppInterfaceImpl.h"

@implementation CppInterfaceImpl

@synthesize value;

-(void) methodA
{
    NSLog(@"Called %s", __FUNCTION__);
}

-(void) methodB
{
    NSLog(@"Called %s", __FUNCTION__);
}

@end



Notice I did not use the .mm extension for this source. It is not necessary for this class but it COULD be for a different one. It might be a good decision to just say all ObjC sources will use the .mm extension in this kind of project so there is no worry about a code or interface change forcing a file extension change.

So I used the features of the language for the getter and setter methods. Otherwise it looks pretty much how you would expect an implementation of the interface to look.

Now for the glue in the middle. The bridge is very simple:

//
//  CppInterfaceOCBridge.h
//  TestCppInterface
//

#ifndef TestCppInterface_CppInterfaceOCBridge_h
#define TestCppInterface_CppInterfaceOCBridge_h

#include "CppInterface.h"

#import "CppInterfaceImpl.h"

class CppInterfaceOCBridge : public CppInterface
{
public:
    CppInterfaceOCBridge();
    
    virtual ~CppInterfaceOCBridge();
    
    virtual void methodA();
    
    virtual void methodB();
    
    virtual int value() const;
    
    virtual void setValue(int value);
    
private:
    CppInterfaceImpl* m_OCObj;
};

inline CppInterfaceOCBridge::CppInterfaceOCBridge()
{
    m_OCObj = [[CppInterfaceImpl alloc] init];
}

inline CppInterfaceOCBridge::~CppInterfaceOCBridge()
{
    [m_OCObj release];
}

inline void CppInterfaceOCBridge::methodA()
{
    [m_OCObj methodA];
}

inline void CppInterfaceOCBridge::methodB()
{
    [m_OCObj methodB];
}

inline int CppInterfaceOCBridge::value() const
{
    return [m_OCObj value];
}

inline void CppInterfaceOCBridge::setValue(int value)
{
    [m_OCObj setValue: value];
}

#endif



With that we are ready to instantiate an Objective-C class as a CppInterface. I created an Objective-C main that does that. While I have been mentioning class factories I did not actually use one in the example. The main is just going to instantiate the bridge and exercise it:

//
//  main.mm
//  TestCppInterface
//

#import <Foundation/Foundation.h>

#include "CppMain.h"
#include "CppInterfaceOCBridge.h"

int main (int argc, const char * argv[])
{
    @autoreleasepool
    {
        CppInterface *a = new CppInterfaceOCBridge;
        
        NSLog(@"Calling C++ methods from within Objective-C!");
        
        a->methodA();
        a->methodB();
        
        a->setValue(5);
        
        NSLog(@"Value is %i", a->value());
        
        CppMain cppMain(*a);
        
        cppMain.run();
        
        delete a;
    }

    return 0;
}


I wanted to go ahead and try passing this class into a method of a plain C++ class that has no ObjC in it (and uses the .cpp extension) so I created the CppMain class and you can see it exercised there. Here is that class:

//
//  CppMain.h
//  TestCppInterface
//

#ifndef TestCppInterface_CppMain_h
#define TestCppInterface_CppMain_h

#include "CppInterface.h"

class CppMain
{
public:
    CppMain(CppInterface& interface);
    
    ~CppMain();
    
    void run();
    
private:
    CppInterface& m_Interface;
};

#endif



//
//  CppMain.cpp
//  TestCppInterface
//

#include <iostream>

#include "CppMain.h"

using namespace std;

CppMain::CppMain(CppInterface& interface) :
    m_Interface(interface)
{
    
}

CppMain::~CppMain()
{
    
}

void CppMain::run()
{
    cout << "Running from CppMain!" << endl;
    
    m_Interface.methodA();
    m_Interface.methodB();
    
    m_Interface.setValue(28);
    
    cout << "Value is " << m_Interface.value() << endl;
}


You might notice a terrible practice in this code related to pointers and reference storing. It was just an experiment. It's going to be okay. So here we can see what we ultimately really want in work. Since most of the code will be C++ and C++ class references will be getting tossed around it is the ability to call the Objective-C class from an unaware C++ class that fulfills the goals of the experiment.

Here is the output when I run this application:

2012-01-16 17:13:20.170 TestCppInterface[8480:707] Calling C++ methods from within Objective-C!
2012-01-16 17:13:20.173 TestCppInterface[8480:707] Called -[CppInterfaceImpl methodA]
2012-01-16 17:13:20.174 TestCppInterface[8480:707] Called -[CppInterfaceImpl methodB]
2012-01-16 17:13:20.175 TestCppInterface[8480:707] Value is 5
Running from CppMain!
2012-01-16 17:13:20.175 TestCppInterface[8480:707] Called -[CppInterfaceImpl methodA]
2012-01-16 17:13:20.176 TestCppInterface[8480:707] Called -[CppInterfaceImpl methodB]
Value is 28
Program ended with exit code: 0


It is very easy to find the C++ output because, unlike the ObjC output, it does not get timestamped.

So as I said, I'm looking to expand on this and see how it might be streamlined for use in a large project. If anyone is aware of another way to achieve the stated goals I would love to see what you have come up with. I think what I have done here has to be one of the first ideas anyone going down this road would consider. I think I will be messing with some ideas for macros in the next few days and I will show what I have come up with at that point. I also am really interested in delving into error handling and supporting exceptions.

Thanks for looking.

Is This A Good Question/Topic? 0
  • +

Page 1 of 1