如何用Objective-C封装一个C++库?

4
我有一个仅包含.h文件的C++库,其中包含数据结构的实现,并希望在我的iPhone应用程序中使用它。
首先,我编写了一个objective-C++的包装器,作为一个类,通过组合拥有一个C++类的实例变量。然后我被"迫"将包装器类的扩展名改为.mm,并且似乎很好。但是,我必须将这个包装类文件包含到几个其他文件中,所以我不得不改变它们的扩展名(以防止一堆编译时错误)。
我正确吗?有没有办法将".mm"扩展名限制在只有几个文件中?(以防止名称冲突等)
编辑:一些更多的信息可能会有所帮助,我正在使用LLVM 1.5作为编译器(我注意到编译时错误的数量从GCC 4.2变化到LLVM 1.5,但我不确定这意味着什么,因为我没有查看所有错误)。
4个回答

4

我的建议是用#ifdefs包装C++代码块:

//MyWrapper.h

#ifdef __cplusplus
class ComposedClass;
#endif 

@interface MyWrapper : NSObject
{
#ifdef __cplusplus
ComposedClass *ptr;
#endif
}

// wrapped methods here...
@end

这是一个略微平庸的PIMPL习惯用法版本,但代码更少,并且对于从你的纯Objective-C代码中隐藏C++-ism很有效。显然,你需要在MyWrapper.mm中包含ComposedClass头文件。
如果ComposedClass是一个模板类型,则需要修改第一个代码块。
#ifdef __cplusplus
#include "ComposedClass.h"
#endif

不要使用前向声明,而是在Objective-C类的实例变量声明中直接使用模板类型。

这种方法是苹果公司运行时专家Greg Parker 建议 的。


我认为那不是一个好主意。任何包含这个头文件的非C++文件都无法“看到”受保护的成员,因此类的大小和实例变量的偏移量将会不同。 - Kristopher Johnson
@Barry Wark 谢谢你,Barry。我认为这是最简单的方法,但似乎我无法实现,因为我得到了编译时错误http://pastebin.com/tp9Zjhss 看起来我漏掉了什么,我以为它们可以是模板,但在你的链接中它们被使用了。 - rano
@rano,抱歉...我错过了头文件。在这种情况下,我会将RTree头文件包含在您的Wrapper .h中(仍然只针对__cplusplus进行#ifdef)。 - Barry Wark
@Barry Wark:感谢您的回答。我已经尝试过了,虽然我的 wrapper.h 中没有更多编译时错误,但在 .mm 文件中却出现了一些新错误,因为当我使用 ivar 时它会说invalid use of incomplete type ...templateClassName...。通过在网上搜索,我发现这可能意味着模板尚未被实例化。有什么建议吗? - rano
1
@Brad Wark:模板实例化是关键(这表明我的C++比你弱:D),谢谢(仅包括头文件的ifdef不足够) - rano
显示剩余2条评论

4
任何包含C++片段的代码(无论多小)都必须使用Objective-C++进行编译(因此应该在.mm文件中)。如果您想减少.mm文件的数量,您需要将C++代码的功能封装在一个Objective-C类中,以便该类的公共接口(其.h文件)仅由Objective-C代码组成。这意味着包装类不能包含C++类型的公共ivar。如果您的C++库只包含数据结构,则不知道这是否是可行的方法。
请注意,据我所知,LLVM 1.5尚未包含C++支持(只有LLVM 2.0才有)。据我所知,当您选择LLVM 1.5时,Xcode会自动为所有C++ / Objective-C ++文件切换到GCC。

2

我不确定我完全理解你的问题,但我认为答案是“是”。

基本上,任何需要引用包装类的东西都是Objective-C ++代码,而不是直接的C ++。

我建议你将包装类的包含限制在实现(.cpp或Objective-C ++等效)文件中。它们不应该导致描述它们实现的类的头文件变成Objective-C ++。

Pimpl惯用语这样的技术可以帮助隔离使用你的类的用户,使他们不会意识到它部分地在Objective-C ++中实现。


+1 我明白你的意思,通过仅在“.mm”文件中包含来限制级联。 - rano
@rano - 看起来你的问题是我描述解决方案的问题的反向问题。而且看起来Objective-C/C++没有将实现概念与类定义分离的概念。这使得工作变得更加困难,需要比我更深入地理解Objective-C/C++。 - Omnifarious
我现在不明白你的意思,Objective-C/C++为类的接口/实现提供了分离。 - rano
@rano - 叹气 哎呀。我觉得我需要学习更多关于Objective-C++的知识,才能更好地帮助你。Pimpl惯用法依赖于类的定义和实现之间的分离才能工作。因此,如果Objective-C++允许这样做,那么就可以将其转换成该语言。 - Omnifarious

1

我偏爱的解决方案是这个前向声明宏:

#define FORWARD(cpp_class) struct cpp_class; typedef struct cpp_class cpp_class;

FORWARD(SomeCppClass);

@interface MyObjcWrapper : NSObject {
    SomeCppClass *ptr;
}

然后MyObjcWrapper.h文件可以安全地包含在Objective-C和Objective-C++文件中。

这是我几年前看到的一篇有用的博客文章 - 现在似乎找不到了。


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