通过元编程简化C++转Objective-C/Cocoa桥接?

33
在一个纯C++的世界中,我们可以使用基于模板的编译时和运行时技术的组合生成不同组件或接口之间的接口或粘合代码(例如,大部分情况下自动转换使用遗留类型的调用)。但是,当需要将C++应用程序与Objective-C/Cocoa用于GUI、系统集成或IPC进行接口处理时,由于类型不够严格,事情变得更加困难,而通常仅需要一个扁平的重复接口层:必须定义薄的桥接代理或编写语言桥接调用的转换代码。
如果必须处理非平凡大小的接口并希望避免基于脚本的代码生成,则这很快变得麻烦,并且每次重构时都很痛苦。使用(模板)元编程和Objective-C运行时库的组合,应该可以大大减少代码量...在我重新发明轮子之前(可能会浪费时间),有人知道在这方面的技术、最佳实践或示例吗?
至于示例,假设我们需要支持这个非正式协议的代理。
- (NSString*)concatString:(NSString*)s1 withString:(NSString*)s2;
- (NSNumber*)     indexOf:(CustomClass*)obj;

我不想现在实现一个Obj-C类并显式地与C++实例建立桥接,而是希望像这样做

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;
    }
};

用户支持额外类型所需的仅是专门化一个转换模板函数:

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

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

// ...
当然,上面的例子忽略了对正式协议等的支持,但应该已经表达了要点。此外,由于Objc运行时类型的类型信息大部分被分解成某些本地类型或类类型,我认为不能避免对委托方法的参数和返回类型进行明确规定。

这是一个非常有趣的问题。我经常做这种包装,虽然我有很多使用的模式,但我从来没有找到过简化模式的元编程方法。看看是否有人有这样的想法会很有趣。C++ 模板不能用于创建 ObjC 类,而 C++ 缺乏足够的运行时内省来动态创建 ObjC 类。但仍然可以使用某种模板语言,甚至是基于宏的。我目前正在为 Cocoaphany(robnapier.net)的下一篇文章研究这个主题,所以我需要更深入地思考。 - Rob Napier
除非我漏看了什么,否则我至少可以通过使用Obj-C运行时(尚未调查类注册部分)来修改和扩展Objective-C类。利用模板检查要绑定到的C++签名等,我在这个方向上并没有看到什么大问题...至少目前还没有,这也是我提问的原因之一 :) - Georg Fritzsche
在我工作的项目中,我们使用以下方法:XML + 外部代码生成器。我不知道这是否适用于您,但无论如何...据我所了解,这种方法一开始很痛苦,但是之后您就不必每次都“重新发明”解决方案了。而且,无论您使用多少种语言,都没有关系。是的,在我们的情况下,XML 文件也是生成的:它可以通过 GUI 前端访问(但当然您始终可以直接更改 XML)。 - avp
我不喜欢外部代码生成,只有在万不得已的情况下才会使用它。有趣的问题是如何使用元编程尽可能避免对代码生成器的需求。我想当我的私人Mac到达时,我将不得不开始测试我的想法。 - Georg Fritzsche
这个问题非常有趣+1,我希望能听到@gf实验的结果。 - Goles
显示剩余2条评论
2个回答

5

我没有找到令人满意的东西,于是想出了一个原型,根据以下非正式协议:

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

并且这段C++代码:

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;
}

允许创建和传递委托:

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()];

然后可以使用:

NSString* result = [delegate concatString:@"abc" withString:@"def"];
assert([result compare:@"abcdef"] == NSOrderedSame);
< p > Boost.Function对象也可以被传递,这意味着Boost.Bind也可以轻松使用。

虽然基本想法是可行的,但这仍然只是一个原型。我在这个主题上写了一篇短小的博客文章,原型源代码可以通过bitbucket获得。欢迎提出有建设性的意见和想法。


4

你看过wxWidgets库吗?虽然我不会用Objective-C编码,但至少开发人员声称该库对Cocoa/Objective-C的支持相当不错。这意味着他们已经以某种方式实现了从C++到Objective-C的映射。该库的网站是http://www.wxwidgets.org


谢谢,似乎有一些通知桥接 - 以后需要再仔细阅读。 - Georg Fritzsche
好的提示,但还不够令人满意。 - Georg Fritzsche

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接