Je travaille sur un projet qui a beaucoup de code C hérité . Nous avons commencé à écrire en C ++, avec l'intention de convertir éventuellement le code hérité également. Je suis un peu confus quant à la façon dont le C et le C ++ interagissent. Je comprends qu'en enveloppant le code C avec extern "C"
le compilateur C ++, les noms du code C ne seront pas modifiés , mais je ne sais pas exactement comment implémenter cela.
Donc, en haut de chaque fichier d'en-tête C (après les gardes d'inclusion), nous avons
#ifdef __cplusplus
extern "C" {
#endif
et en bas on écrit
#ifdef __cplusplus
}
#endif
Entre les deux, nous avons tous nos inclus, typedefs et prototypes de fonctions. J'ai quelques questions, pour voir si je comprends bien:
Si j'ai un fichier C ++ A.hh qui comprend un fichier d'en-tête C Bh, comprend un autre fichier d'en-tête C Ch, comment cela fonctionne-t-il? Je pense que lorsque le compilateur
__cplusplus
entrera dans Bh, sera défini, donc il encapsulera le code avecextern "C"
(et__cplusplus
ne sera pas défini à l'intérieur de ce bloc). Ainsi, quand il__cplusplus
entrera dans Ch, il ne sera pas défini et le code ne sera pas encapsuléextern "C"
. Est-ce correct?Y a-t-il quelque chose de mal à envelopper un morceau de code avec
extern "C" { extern "C" { .. } }
? Que fera le secondextern "C"
?Nous ne mettons pas ce wrapper autour des fichiers .c, seulement les fichiers .h. Alors, que se passe-t-il si une fonction n'a pas de prototype? Le compilateur pense-t-il que c'est une fonction C ++?
Nous utilisons également du code tiers qui est écrit en C , et n'a pas ce genre d'enveloppe autour. Chaque fois que j'inclus un en-tête de cette bibliothèque, j'ai mis un
extern "C"
autour de #include. Est-ce la bonne façon de gérer cela?Enfin, cette configuration est-elle une bonne idée? Y a-t-il autre chose que nous devrions faire? Nous allons mélanger C et C ++ dans un avenir prévisible, et je veux m'assurer que nous couvrons toutes nos bases.
la source
To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed.
(je l'ai obtenue du lien )Réponses:
extern "C"
ne change pas vraiment la façon dont le compilateur lit le code. Si votre code est dans un fichier .c, il sera compilé en C, s'il est dans un fichier .cpp, il sera compilé en C ++ (sauf si vous faites quelque chose d'étrange à votre configuration).Ce
extern "C"
qui affecte le lien. Les fonctions C ++, une fois compilées, voient leur nom modifié - c'est ce qui rend possible la surcharge. Le nom de la fonction est modifié en fonction des types et du nombre de paramètres, de sorte que deux fonctions portant le même nom auront des noms de symboles différents.Le code à l'intérieur d'un
extern "C"
est toujours du code C ++. Il y a des limites à ce que vous pouvez faire dans un bloc "C" externe, mais elles concernent toutes la liaison. Vous ne pouvez pas définir de nouveaux symboles qui ne peuvent pas être créés avec la liaison C. Cela signifie pas de classes ou de modèles, par exemple.extern "C"
les blocs se nichent bien. Il y a aussiextern "C++"
si vous vous retrouvez désespérément piégé à l'intérieur desextern "C"
régions, mais ce n'est pas une si bonne idée du point de vue de la propreté.Maintenant, concernant spécifiquement vos questions numérotées:
Concernant # 1: __cplusplus restera défini à l'intérieur des
extern "C"
blocs. Cela n'a pas d'importance, cependant, car les blocs doivent s'imbriquer parfaitement.Concernant # 2: __cplusplus sera défini pour toute unité de compilation exécutée via le compilateur C ++. En règle générale, cela signifie les fichiers .cpp et tous les fichiers inclus par ce fichier .cpp. Le même .h (ou .hh ou .hpp ou ce que vous avez) pourrait être interprété comme C ou C ++ à différents moments, si différentes unités de compilation les incluent. Si vous voulez que les prototypes du fichier .h fassent référence aux noms de symboles C, ils doivent en avoir
extern "C"
lorsqu'ils sont interprétés en C ++, et ils ne devraient pas en avoirextern "C"
lorsqu'ils sont interprétés en C - d'où la#ifdef __cplusplus
vérification.Pour répondre à votre question n ° 3: les fonctions sans prototypes auront une liaison C ++ si elles sont dans des fichiers .cpp et non à l'intérieur d'un
extern "C"
bloc. C'est bien, cependant, car s'il n'a pas de prototype, il ne peut être appelé que par d'autres fonctions dans le même fichier, et vous ne vous souciez généralement pas de l'apparence de la liaison, car vous ne prévoyez pas d'avoir cette fonction être appelé de toute façon en dehors de la même unité de compilation.Pour # 4, vous l'avez exactement. Si vous incluez un en-tête pour le code qui a une liaison C (tel que le code qui a été compilé par un compilateur C), alors vous devez
extern "C"
l'en-tête - de cette façon, vous pourrez vous lier à la bibliothèque. (Sinon, votre éditeur de liens chercherait des fonctions avec des noms comme_Z1hic
lorsque vous cherchiezvoid h(int, char)
5: Ce type de mélange est une raison courante à utiliser
extern "C"
, et je ne vois rien de mal à le faire de cette façon - assurez-vous simplement de comprendre ce que vous faites.la source
extern "C++"
quand votre en-tête / code C ++ est piégé profondément dans du code C__cplusplus
est de déterminer siC++
est utilisé vsC
, donc le définir manuellement / explicitement défie le but de celui-ci ...extern "C"
ne modifie pas la présence ou l'absence de la__cplusplus
macro. Il modifie simplement le lien et le changement de nom des déclarations encapsulées.Vous pouvez emboîter des
extern "C"
blocs très heureusement.Si vous compilez vos
.c
fichiers en C ++, tout ce qui n'est pas dans unextern "C"
bloc et sansextern "C"
prototype sera traité comme une fonction C ++. Si vous les compilez en C, alors bien sûr, tout sera une fonction C.Oui
Vous pouvez mélanger C et C ++ en toute sécurité de cette manière.
la source
.c
fichiers en C ++, tout est compilé en code C ++, même s'il est dans unextern "C"
bloc. Leextern "C"
code ne peut pas utiliser des fonctionnalités qui dépendent des conventions d'appel C ++ (par exemple la surcharge de l'opérateur) mais le corps de la fonction est toujours compilé en C ++, avec tout ce que cela implique.Un couple de pièges qui sont des complices de l'excellente réponse d'Andrew Shelansky et en désaccord un peu avec ne change pas vraiment la façon dont le compilateur lit le code
Parce que vos prototypes de fonctions sont compilés en C, vous ne pouvez pas avoir une surcharge des mêmes noms de fonctions avec des paramètres différents - c'est l'une des principales caractéristiques du changement de nom du compilateur. Il est décrit comme un problème de liaison, mais ce n'est pas tout à fait vrai - vous obtiendrez des erreurs à la fois du compilateur et de l'éditeur de liens.
Les erreurs du compilateur seront si vous essayez d'utiliser les fonctionnalités C ++ de la déclaration de prototype telles que la surcharge.
Les erreurs de l'éditeur de liens se produiront plus tard, car votre fonction semblera introuvable, si vous n'avez pas le wrapper externe "C" autour des déclarations et que l'en-tête est inclus dans un mélange de sources C et C ++.
L'une des raisons pour dissuader les gens d'utiliser le compilé C en tant que paramètre C ++ est que cela signifie que leur code source n'est plus portable. Ce paramètre est un paramètre de projet et donc si un fichier .c est déposé dans un autre projet, il ne sera pas compilé en c ++. Je préfère que les gens prennent le temps de renommer les suffixes de fichiers en .cpp.
la source
Il s'agit de l'ABI, afin de permettre aux applications C et C ++ d'utiliser les interfaces C sans aucun problème.
Étant donné que le langage C est très facile, la génération de code a été stable pendant de nombreuses années pour différents compilateurs, tels que GCC, Borland C \ C ++, MSVC, etc.
Alors que C ++ devient de plus en plus populaire, beaucoup de choses doivent être ajoutées dans le nouveau domaine C ++ (par exemple, finalement, le Cfront a été abandonné chez AT&T parce que C ne pouvait pas couvrir toutes les fonctionnalités dont il avait besoin). Telles que la fonctionnalité de modèle et la génération de code au moment de la compilation, par le passé, les différents fournisseurs de compilateurs ont effectivement mis en œuvre séparément le compilateur et l'éditeur de liens C ++, les ABI réels ne sont pas du tout compatibles avec le programme C ++ sur différentes plates-formes.
Les gens pourraient toujours aimer implémenter le programme réel en C ++ mais garder toujours l'ancienne interface C et ABI comme d'habitude, le fichier d'en-tête doit déclarer extern "C" {} , il indique au compilateur de générer un C ABI compatible / ancien / simple / facile pour les fonctions d'interface si le compilateur est un compilateur C et non un compilateur C ++.
la source