用Objective-C封装C++库并不能隐藏C++符号

5
我正在尝试用Objective-C来包装一个C++库(用于Sybase Ultralite),以便该库可以被导入到MonoTouch中。我创建了一个Objective-C库,并在该项目中包含了编译好的C++库,libulrt.a。为了使我的项目编译通过,我将用户头文件搜索路径设置为指向C++头文件所在的目录。然后,我不得不将编译源代码设置为Objective-C++
现在的问题是,虽然包装库正确地编译了,但一旦我将其包含到另一个Xcode项目中,我就必须再次将编译源代码设置为Objective-C++,否则使用我的包装库的项目会出现链接错误。我不明白为什么会这样,因为我的包装库的头文件只包含Objective-C代码,根本没有C++代码。C++代码只出现在包装库的实现(*.mm文件)中。我需要做什么才能使C++实现对使用包装库的项目完全透明?换句话说,我想能够包含我的包装库并将编译源代码设置为Objective-C进行编译。有什么想法如何做到这一点吗?
当我编译使用我的包装库的项目时,我遇到的链接错误如下: (由于错误列表太长,我把它缩短了!)
"operator delete(void*)", referenced from:
zc3db40339fee::~zc3db40339fee()in libUltralite.a(ee39bf4763.o)
zb4297ee7d543::~zb4297ee7d543()in libUltralite.a(747e80fdad.o)
z33836a0a6f46::~z33836a0a6f46()in libUltralite.a(f240efda30.o)
"___cxa_pure_virtual", referenced from:
vtable for ze78b0ec59364in libUltralite.a(2c50e8e8ff.o)
vtable for ze78b0ec59364in libUltralite.a(2c50e8e8ff.o)
vtable for ze78b0ec59364in libUltralite.a(2c50e8e8ff.o)

我的包装库的头文件如下所示:

#import <Foundation/Foundation.h>

@interface DataAccess : NSObject {}

// Release objects.
- (void)dealloc;

// Singleton instance of the DataAccess class. 
+ (DataAccess*)sharedInstance; 

// Finalize the Database Manager when done with the DB.
+ (void)fini;

// Adds the given name to the database. 
- (void)addName:(NSString *)name;
@end 

有没有办法在编译时将这个库以Objective-C++的形式进行编译,但是在项目中使用时仍能被视为Objective-C呢?
3个回答

6
@Mark Bessey的想法是正确的,但他错误地认为设置语言和添加C++库之间几乎没有区别。添加C++库是便宜且必要的(无论如何都需要)。更改语言是昂贵的,因为ObjC++编译器速度较慢,并且在我的经验中,生成的代码在通过Xcode中的gdb进行处理时有点麻烦。
您只需要在使用的项目中将-lstdc++添加到LD_FLAGS(其他链接器标志)中即可。确保"C++ Standard Library Type"设置为动态。

我删除了第一段,因为它的措辞不太好。鉴于Bruce想要一个没有指向libstdc++的悬空引用的库,他需要使用静态版本。请查看我的编辑后的回复。 - Mark Bessey
我不相信他想要链接静态的C++库。我非常确定他想要链接动态的C++库。这只需要在最后的链接步骤中完成,而不是在库构建中完成。静态库无法链接任何东西(静态库基本上只是一堆.o文件连接在一起)。 - Rob Napier
谢谢。这正是我需要的信息。我需要做的是让我的包装库被一个MonoTouch项目所使用,在MonoTouch中没有选择将源代码编译为Objective-C++的选项。这就是为什么我需要使用ObjC编译器来编译消费项目。我先在Xcode中进行了测试,发现它可以工作。然后,因为MonoTouch还有一个额外链接器标志的项目选项,我将-lstdc++添加到此选项中,我的MonoTouch项目编译成功了。:)感谢您的帮助。 - BruceHill
汗水。因为把C++库包装得太好,以至于C++标准库被彻底搞糊涂而受到惩罚是一件不好玩的事情。感谢建议。 - user234736

1

看起来你需要将C++运行时库与你的包装库静态链接,才能真正解决这个问题。但如果库的客户端使用Objective-C++,那么你可能会遇到问题,因为同一个程序中会有两个版本的C++标准库。


一个 MacOS 项目的快速测试似乎表明,您需要将 C++ 库类型设置为“静态”,并可能还需要设置“默认情况下隐藏符号”。

嗨,马克。感谢你的回答。你说的关于需要静态链接C++运行时库似乎是有道理的。只是我对Objective C和Xcode非常陌生,不知道该在_linker flags_中添加什么标志来完成这个操作。如果你能告诉我如何做到这一点,我会尝试一下。使用Objective-C++的客户端的问题根本不是问题,因为在我的情况下,客户端将是MonoTouch btouch工具,它只能与Objective-C一起使用。 - BruceHill
将符号默认设置为隐藏通常是良好的卫生习惯,但我不认为这是解决这个问题的一部分。他的问题是找不到符号,而不是太多符号可见。 - Rob Napier

1
你需要将你的包装器构建为共享对象,而不是静态库。静态库没有链接,函数的解析(特别是你所包装的代码使用的C++标准库函数)发生在链接时。

这是iPhone,所以他不能使用共享库。在Mac上,他只应该不使用共享库。捆绑包中的共享库会带来很多麻烦,并增加捆绑包的大小(因为您无法进行死代码剥离)。根据我的经验,如果您实际上不在程序之间共享代码,则更喜欢静态库。 - Rob Napier
@Rob:嗯,除非 Mac 与其他 BSD 版本非常不同,否则所需的函数根本不会成为其静态包装库的一部分,因为它尚未链接。 因此,客户端需要提供它们。 在 Visual C++ 中,我可以使用“#pragma comment(lib,“ libname”)”来自动链接必备库,苹果编译器是否有类似的东西? - Ben Voigt
在Mac(和BSD)上,静态库只是一堆粘在一起的.o文件。因此,您需要在应用程序的最终链接中列出所有其他库(与动态库不同,后者可以包含对其自身依赖项的引用)。在这种情况下,您可以通过传递-lstdc++来实现。不幸的是,没有与您列出的#pragma相当的东西。不过,问题是关于iPhone的,它无论如何都不支持第三方共享库。 - Rob Napier
@Rob:动态库是通过链接创建的,因此它们通常不会“包含对其自身依赖项的引用”,而是将依赖项合并在一起(除非依赖项本身是共享库,但这里每个人都在谈论静态libstdc++)。至于iPhone方面,我只能向您致敬……我没有iPhone,也没有为之开发,我只是试图根据苹果当前操作系统的BSD遗产推断其行为。 - Ben Voigt
“只是一堆目标文件”是所有主要平台上静态库的一个好描述。通常还会有一个符号目录,以提高链接器的效率,但这并不从根本上改变其行为。 - Ben Voigt
@Ben,感谢你帮我理解问题出在哪里。我错误地认为C++库是在包装库的编译时链接的。这次讨论帮助我澄清了这个误解。 - BruceHill

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