使用 Objective-C++ 和 Objective-C 的链接器问题
Linker problem using Objective-C++ with Objective-C
目前我有一个 iOS 项目正在使用 Objective-C。出于性能原因,我实现了 Objective-C++ 代码,如下所示:
base11.mm
#include <string>
#include "base11.h"
static const std::string characters = "0123456789_";
const char* to_base11(long n) {
// some c++ code here
}
long from_base11(const char* input) {
// some c++ code here
}
base11.h
#ifndef base11_h
#define base11_h
#include <stdio.h>
const char* to_base11(long n);
long from_base11(const char* input);
#endif /* base11_h */
当然,我为 class 使用了 .mm
扩展,我只使用普通 C 函数参数和 return 类型。
然后我有一个 Objective-C class 将这些 C++ 函数包装在以下方法中:
Util.m
#include "base11.h"
+(NSString*) convertBase10ToBase11:(long) base10 {
const char* base11 = to_base11(base10);
return [NSString stringWithUTF8String:base11];
}
+(NSNumber*) convertBase11ToBase10:(NSString*) base11 {
const char* input = [base11 UTF8String];
return @(from_base11(input));
}
和Util.h:
@interface Util : NSObject
+(NSString*) convertBase10ToBase11:(long) base10;
+(NSNumber*) convertBase11ToBase10:(NSString*) base11;
@end
然而,这会产生以下编译错误:
Undefined symbols for architecture x86_64:
"_from_base11", referenced from:
+[Util convertBase11ToBase10:] in Util.o
"_to_base11", referenced from:
+[Util convertBase10ToBase11:] in Util.o
ld: symbol(s) not found for architecture x86_64
然而,这很奇怪。我选择不对 Util.m
class 使用 .mm
扩展,因为我不在那里使用任何 C++ 代码。另请注意,base11.h
头文件也未使用任何 C++ 代码。那么,为什么会出现此错误?按照这个post我的做法应该是正确的:
How to call a method on .mm file from a objective c class
这是函数符号名的问题。
C++ 允许具有相同名称和不同原型的函数。
因此,当使用 C++/Objective-C++ 编译的函数时,它会获得描述它原型的符号名称并使其独一无二。
所以你需要强制编译器使用C的符号名。
您可以通过在函数原型之前添加 extern "C"
来实现。
但是如果你在C和C++源文件中都使用这个头文件就不太方便了,因为C不知道它是什么extern "C"
.
幸运的是CoreFoundation
已经解决了这个问题。你可以看看他们是如何宣布他们的 CFString.h
, CFArray.h
.
所以你只需要使用 CoreFoundation 的宏:CF_EXTERN_C_BEGIN
和 CF_EXTERN_C_END
.
#ifndef base11_h
#define base11_h
#include <stdio.h>
#include <CoreFoundation/CFBase.h>
CF_IMPLICIT_BRIDGING_ENABLED
CF_EXTERN_C_BEGIN
const char* to_base11(long n);
long from_base11(const char* input);
CF_EXTERN_C_END
CF_IMPLICIT_BRIDGING_DISABLED
#endif /* base11_h */
目前我有一个 iOS 项目正在使用 Objective-C。出于性能原因,我实现了 Objective-C++ 代码,如下所示:
base11.mm
#include <string>
#include "base11.h"
static const std::string characters = "0123456789_";
const char* to_base11(long n) {
// some c++ code here
}
long from_base11(const char* input) {
// some c++ code here
}
base11.h
#ifndef base11_h
#define base11_h
#include <stdio.h>
const char* to_base11(long n);
long from_base11(const char* input);
#endif /* base11_h */
当然,我为 class 使用了 .mm
扩展,我只使用普通 C 函数参数和 return 类型。
然后我有一个 Objective-C class 将这些 C++ 函数包装在以下方法中:
Util.m
#include "base11.h"
+(NSString*) convertBase10ToBase11:(long) base10 {
const char* base11 = to_base11(base10);
return [NSString stringWithUTF8String:base11];
}
+(NSNumber*) convertBase11ToBase10:(NSString*) base11 {
const char* input = [base11 UTF8String];
return @(from_base11(input));
}
和Util.h:
@interface Util : NSObject
+(NSString*) convertBase10ToBase11:(long) base10;
+(NSNumber*) convertBase11ToBase10:(NSString*) base11;
@end
然而,这会产生以下编译错误:
Undefined symbols for architecture x86_64:
"_from_base11", referenced from:
+[Util convertBase11ToBase10:] in Util.o
"_to_base11", referenced from:
+[Util convertBase10ToBase11:] in Util.o
ld: symbol(s) not found for architecture x86_64
然而,这很奇怪。我选择不对 Util.m
class 使用 .mm
扩展,因为我不在那里使用任何 C++ 代码。另请注意,base11.h
头文件也未使用任何 C++ 代码。那么,为什么会出现此错误?按照这个post我的做法应该是正确的:
How to call a method on .mm file from a objective c class
这是函数符号名的问题。 C++ 允许具有相同名称和不同原型的函数。 因此,当使用 C++/Objective-C++ 编译的函数时,它会获得描述它原型的符号名称并使其独一无二。
所以你需要强制编译器使用C的符号名。
您可以通过在函数原型之前添加 extern "C"
来实现。
但是如果你在C和C++源文件中都使用这个头文件就不太方便了,因为C不知道它是什么extern "C"
.
幸运的是CoreFoundation
已经解决了这个问题。你可以看看他们是如何宣布他们的 CFString.h
, CFArray.h
.
所以你只需要使用 CoreFoundation 的宏:CF_EXTERN_C_BEGIN
和 CF_EXTERN_C_END
.
#ifndef base11_h
#define base11_h
#include <stdio.h>
#include <CoreFoundation/CFBase.h>
CF_IMPLICIT_BRIDGING_ENABLED
CF_EXTERN_C_BEGIN
const char* to_base11(long n);
long from_base11(const char* input);
CF_EXTERN_C_END
CF_IMPLICIT_BRIDGING_DISABLED
#endif /* base11_h */