views:

665

answers:

2

In a pure C++ world we can generate interfacing or glue code between different components or interfaces at compile time, using a combination of template-based compile-time and runtime-techniques (to e.g. mostly automatically marshall to/from calls using legacy types).

When having to interface C++ applications with Objective-C/Cocoa for GUI, system integration or IPC though, things become harder due to the less strict typing - yet often not more then a flat repitive interface layer is needed: thin bridging delegates have to be defined or conversion code to language bridging calls has to be written.

If you have to deal with interfaces of non-trivial size and want to avoid script-based code generation this quickly becomes cumbersome and is just a pain every time refactorings have to take place. Using a combination of (template) metaprogramming and the Objective-C runtime library, it should be possible to reduce the amount of code considerably...

Before i go to reinvent the wheel (and possibly waste time), does anyone know about techniques, best-practices or examples in that direction?


As for an example, lets say we need a delegate that supports this informal protocol:

- (NSString*)concatString:(NSString*)s1 withString:(NSString*)s2;
- (NSNumber*)     indexOf:(CustomClass*)obj;

Instead of implementing an Obj-C class now that explicitly bridges to a C++-instance, i'd like to do something like this instead:

class CppObj {
    ObjcDelegate m_del;
public:
    CppObj() : m_del(this) 
    {
        m_del.addHandler
            <NSString* (NSString*, NSString*)>
            ("concatString", &CppObj::concat);
        m_del.addHandler
            <NSNumber* (CustomClass*)>
            ("indexOf", &CppObj::indexOf);
    }

    std::string concat(const std::string& s1, const std::string& s2) {
        return s1.append(s2);
    }

    size_t indexOf(const ConvertedCustomClass& obj) {
        return 42;
    }
};

All that should be needed from the user to support additional types would be to specialize a conversion template function:

template<class To, class From> To convert(const From&);

template<> 
NSString* convert<NSString*, std::string>(const std::string& s) { 
    // ...
}

// ...

The example above of course does ignore support for formal protocols etc. but should get the point across. Also, due to the type-information for Objc-runtime-types being mostly decayed into some-native-types or class-type i don't think the explicit specification of parameter and return types for the delegate-methods can be avoided.

+3  A: 

Did you look at the wxWidgets library? I don't code in Objective-C, but at least the developers claim decent support for Cocoa/Objective-C. Which means, they have some mapping from C++ implemented somehow. The library's website is http://www.wxwidgets.org.

Kerido
Thanks, there seems to be some notification-bridging - will have to read more into it later.
Georg Fritzsche
Good hint, but wasn't quite satisfactory.
Georg Fritzsche
+1  A: 

I didn't find anything satisfactory and came up with a prototype that, given the following informal protocol:

- (NSString*)concatString:(NSString*)s1 withString:(NSString*)s2;

and this C++ code:

struct CppClass {
    std::string concatStrings(const std::string& s1, const std::string& s2) const {
        return s1+s2;
    }
};

std::string concatStrings(const std::string& s1, const std::string& s2) {
    return s1+s2;
}

allows creating and passing a delegate:

CppClass cpp;
og::ObjcClass objc("MyGlueClass");
objc.add_handler<NSString* (NSString*, NSString*)>
    ("concatString:withString:", &cpp, &CppClass::concatStrings);
// or using a free function:
objc.add_handler<NSString* (NSString*, NSString*)>
    ("concatString:withString:", &concatStrings);
[someInstance setDelegate:objc.get_instance()];

which can then be used:

NSString* result = [delegate concatString:@"abc" withString:@"def"];
assert([result compare:@"abcdef"] == NSOrderedSame);

Boost.Function objects can also be passed, which means Boost.Bind can easily be used as well.

While the basic idea works, this is still a prototype. I did a short blog post on the subject and the prototype source is available via bitbucket. Constructive input and ideas welcome.

Georg Fritzsche