我试图找出为什么当我将main.m文件转换为main.mm文件时,它不再能正常链接。
我已将问题缩减为以下示例代码:
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
int main( int argc, const char ** argv ) {
return NSApplicationMain( argc, argv);
}
我正在使用gnustep和linux。我输入以下命令,一切都按预期工作:
g ++ -g -c main.m -I / usr / GNUstep / Local / Library / Headers -I / usr / GNUstep / System / Library / Headers
g ++ -g -o test main.o -L / usr / GNUstep / Local / Library / Libraries -L / usr / GNUstep / System / Library / Libraries -lgnustep-base -lgnustep-gui
现在如果我将main.m重命名为main.mm并使用这两个命令(同样exept main.m now main.mm):
g ++ -g -c main.mm -I / usr / GNUstep / Local / Library / Headers -I / usr / GNUstep / System / Library / Headers
g ++ -g -o test main.o -L / usr / GNUstep / Local / Library / Libraries -L / usr / GNUstep / System / Library / Libraries -lgnustep-base -lgnustep-gui
我收到以下错误: main.mm:7:未定义引用`NSApplicationMain(int,char const **)'
有人可以找到我做错的事吗?我不明白为什么它现在无法链接。
我正在尝试将一些C ++类添加到目标c程序中,这使我无法继续。
感谢您提供任何帮助。
答案 0 :(得分:9)
问题在于,当你将它编译为C ++时,符号NSApplicationMain
的编译器mangles the name,因此无法找到它,因为它正在寻找像__Z17NSApplicationMainiPPKc
这样的东西。您可以使用nm
程序(来自binutils)查看目标文件引用的符号:
$ # When compiled as Objective-C:
$ nm main.o | grep NSApplicationMain
U NSApplicationMain
$ # When compiled as Objective-C++:
$ nm main.o | grep NSApplicationMain
U _Z17NSApplicationMainiPPKc
为了避免这个问题,需要使用extern "C"
修饰符声明C函数,以告诉编译器不要破坏名称。查看<AppKit/NSApplication.h>
,声明NSApplicationMain
的头文件,我看到了:
APPKIT_EXPORT int
NSApplicationMain(int argc, const char **argv);
唉,APPKIT_EXPORT
被定义为extern
,__declspec(dllexport)
,extern __declspec(dllexport)
中的一个,或<AppKit/AppKitDefines.h>
中的任何内容。因为它也用于全局变量声明,所以我们无法通过将其重新定义为extern "C"
来解决这个问题(无论如何这将非常hacky和kludgy)。 AppKit头文件似乎根本不包含任何extern "C"
声明,但我确实在Foundation/
和GNUStepBase/
下的各种头文件中看到它们。
那你能做什么?解决方案是使用extern "C"
包装您的包含:
extern "C"
{
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
}
int main( int argc, const char ** argv ) {
return NSApplicationMain( argc, argv);
}
这将为这些头文件中定义的函数提供正确的链接,并且它将全部解决。但你不应该这样做 - 我会用GNUstep提交一个bug报告,告诉他们在头文件中添加正确的extern "C"
声明。
答案 1 :(得分:4)
问题是名称修改c ++编译器通常用于在链接阶段启用函数重载。
C ++定义了一个extern "C"
指令,强制它为函数使用C兼容名称。
您可以在C ++文件中使用它,如下所示: -
// this makes func use a C compatible linkage
extern "C" void func(int a)
{
在C和C ++包含的头文件中,必须保护C中的extern“C”声明,并且C编译器不会支持它。
#ifndef EXTERN_C
#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C
#endif
#endif
// Use it like this to declare a Function with C linkage
EXTERN_C void func(int a);
// If you have a lot of functions and declarations that need to be C compatible
#ifdef __cplusplus
extern "C" {
#endif
//functions with C linkage
void func(int a);
...
#ifdef __cplusplus
}
#endif
现在,这对您的问题有何帮助?好吧,main.mm表示Foundation.h和AppKit.h文件正在编译为C ++。为什么Apple没有使用外部“C”指令来保护NSApplicationMain我无法猜测,但它显然没有受到保护。
一个简单的,如果是残酷的修复,就是改变你的#imports,如下所示:
extern "C" {
// All declarations inside this block will use C linkage
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
}
int main( int argc, const char ** argv ) {
return NSApplicationMain( argc, argv);
}