3

我试图弄清楚为什么当我将 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 现在 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: undefined reference to `NSApplicationMain(int, char const**)'

有人可以找到我做错了什么吗?我不明白为什么它现在无法链接。

我正在尝试将一些 C++ 类添加到目标 c 程序中,这使我无法继续。

感谢您提供任何帮助。

4

2 回答 2

9

问题是当你将它编译为 C++ 时,编译器会破坏符号的名称NSApplicationMain,所以它找不到它,因为它正在寻找类似__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 提交一个错误报告,告诉他们在extern "C"他们的头文件中添加正确的声明。

于 2010-01-03T17:25:41.887 回答
4

问题是修饰 c++ 编译器通常用于在链接阶段启用函数重载的名称。

C++ 定义了一个extern "C"指令,强制它为函数使用与 C 兼容的名称。

您可以像这样在 C++ 文件中使用它:-

  // this makes func use a C compatible linkage
  extern "C" void func(int a)
  {

在 C 和 C++ 包含的头文件中,有必要保护 extern "C" 声明不受 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 没有使用 extern "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);
}
于 2010-01-03T17:23:58.017 回答