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 に変更し、これら 2 つのコマンドを使用すると (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 プログラムに追加しようとしていますが、これが続行を妨げています。

ご協力いただきありがとうございます。

4

2 に答える 2

9

問題は、C++ としてコンパイルすると、コンパイラがシンボルの名前NSApplicationMainを壊してしまい、__Z17NSApplicationMainiPPKc. プログラム (binutils から) を使用してnm、オブジェクト ファイルが参照しているシンボルを確認できます。

$ # 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"(とにかく非常にハックで厄介です)。extern "C"AppKit ヘッダー ファイルには、宣言がまったく含まれていないように見えますが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 に答える