2010-01-03 7 views
3

J'essaie de comprendre pourquoi lorsque je convertis mon fichier main.m en un fichier main.mm, il ne sera plus lié correctement.objectif de liaison C++

J'ai réduit le problème à l'exemple de code suivant:

#import <Foundation/Foundation.h> 
#import <AppKit/AppKit.h> 
int main(int argc, const char ** argv) { 
return NSApplicationMain(argc, argv); 
} 

J'utilise GNUstep et linux. J'entre les commandes suivantes et tout fonctionne comme prévu:

g ++ -c -g main.m -I/usr/GNUstep/Local/Library/En-têtes -I/usr/GNUstep/System/Library/En-têtes

g ++ -g -o test main.o -L /usr/ GNUstep/Local/Bibliothèque/Bibliothèques -L /usr/ GNUstep /Système/Bibliothèque/Bibliothèques -lgnustep-base -lgnustep-gui

Maintenant, si je renommer main.m à main.mm et utilisez ces deux commandes (même main sauf main.mm maintenant):

g ++ -g -c main.mm -I/usr/GNUstep/Local/Bibliothèque/En-têtes -I/usr/GNUstep/System/Library/En-têtes

g ++ -g -o Test main.o -L/usr/GNUstep/Local/Library/Bibliothèques -L/usr/GNUstep/System/Library/Bibliothèques base -lgnustep de -lgnustep-IUG

Je reçois l'erreur suivante: main.mm:7: référence non définie à `NSApplicationMain (int, char const **) '

Quelqu'un peut-il trouver ce que je fais mal? Je ne vois pas pourquoi il ne parvient pas à établir un lien. J'essaye d'ajouter quelques classes de C++ à un programme d'objectif c et ceci m'empêche de continuer.

Merci pour toute aide que vous pouvez fournir.

Répondre

9

Le problème est que lorsque vous compilez comme C++, le compilateur mangles the name du symbole NSApplicationMain, il peut ne le trouve pas, car il cherche quelque chose comme __Z17NSApplicationMainiPPKc.Vous pouvez utiliser le programme nm (de binutils) pour voir quels symboles les fichiers objets font référence:

$ # When compiled as Objective-C: 
$ nm main.o | grep NSApplicationMain 
       U NSApplicationMain 
$ # When compiled as Objective-C++: 
$ nm main.o | grep NSApplicationMain 
       U _Z17NSApplicationMainiPPKc 

Pour éviter ce problème, les fonctions C doivent être déclarées avec un modificateur extern "C" pour dire au compilateur ne pas démolir le nom. En regardant dans <AppKit/NSApplication.h>, le fichier d'en-tête où NSApplicationMain est déclaré, je vois ceci:

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

Hélas, APPKIT_EXPORT est défini comme l'un des extern, __declspec(dllexport), extern __declspec(dllexport), ou rien dans <AppKit/AppKitDefines.h>. Comme il est également utilisé pour les déclarations de variables globales, nous ne pouvons pas contourner cela en le redéfinissant à extern "C" (ce qui serait extrêmement hacky et kludgy de toute façon). Les fichiers d'en-tête AppKit ne semblent contenir aucune déclaration extern "C", bien que je les vois dans divers fichiers d'en-tête sous Foundation/ et GNUStepBase/.

Alors, que pouvez-vous faire? La solution consiste à envelopper votre comprend avec un extern "C":

extern "C" 
{ 
#import <Foundation/Foundation.h> 
#import <AppKit/AppKit.h> 
} 

int main(int argc, const char ** argv) { 
    return NSApplicationMain(argc, argv); 
} 

Cela donnera les fonctions définies dans les en-tête des fichiers le lien approprié, et il fonctionnera tout. Mais vous ne devriez pas avoir à faire cela - je déposerais un rapport de bug avec GNUstep, en leur disant d'ajouter des déclarations correctes à leurs fichiers d'en-tête.

4

Le problème est le nom que les compilateurs C++ mangling utilisent généralement pour activer la surcharge de fonctions au niveau de la liaison.

C++ définit une directive extern "C" qui l'oblige à utiliser un nom compatible C pour une fonction.

Vous pouvez l'utiliser dans un C++ fichier comme ceci: -

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

Dans un fichier d'en-tête qui est inclus par C et C++, il est nécessaire de protéger la extern déclaration « C » de C, et C compilateur ne le supporte pas.

#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 

Maintenant, comment cela aide-t-il votre problème? Eh bien, main.mm signifie que les fichiers Foundation.h et AppKit.h sont compilés en C++. Pourquoi Apple n'a pas protégé NSApplicationMain avec une directive externe "C" que je ne peux pas deviner, mais elle n'est clairement pas gardée.

Un simple, si brutale, fixer serait de modifier vos #imports aiment suit:

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); 
}