【问题标题】:linking objective c++链接目标c ++
【发布时间】:2011-01-01 00:28:12
【问题描述】:

我试图弄清楚为什么当我将 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: 对 `NSApplicationMain(int, char const**)' 的未定义引用

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

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

感谢您提供的任何帮助。

【问题讨论】:

    标签: c++ objective-c linker gnustep


    【解决方案1】:

    问题在于修改 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);
    }
    

    【讨论】:

      【解决方案2】:

      问题是当你把它编译成C++时,编译器mangles the name的符号是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
      

      为了避免这个问题,C 函数需要用extern "C" 修饰符声明,以告诉编译器不要破坏名称。查看&lt;AppKit/NSApplication.h&gt;,声明NSApplicationMain 的头文件,我看到了:

      APPKIT_EXPORT int
      NSApplicationMain(int argc, const char **argv);
      

      唉,APPKIT_EXPORT 被定义为 extern__declspec(dllexport)extern __declspec(dllexport) 之一,或者在 &lt;AppKit/AppKitDefines.h&gt; 中没有任何内容。由于它也用于全局变量声明,因此我们无法通过将其重新定义为 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" 声明。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-03-21
        • 1970-01-01
        • 2014-11-07
        • 1970-01-01
        • 1970-01-01
        • 2013-07-28
        • 2014-03-25
        相关资源
        最近更新 更多