Combiner C++ et C-comment fonctionne # ifdef cplusplus?

, je travaille sur un projet qui a beaucoup d'héritage C le code. Nous avons commencé à écrire en C++, avec l'intention de convertir éventuellement le code hérité, ainsi. Je suis un peu confus sur la façon dont les C et C++ interagissent. Je comprends qu'en enveloppant le code C avec extern "C" le compilateur C++ ne mangera pas les noms du code C, mais je ne suis pas tout à fait sûr de savoir comment l'implémenter.

Donc, en haut de chaque fichier d'en-tête C (après le inclure les gardes), nous avons

#ifdef __cplusplus
extern "C" {
#endif

Et en bas, nous écrivons

#ifdef __cplusplus
}
#endif

Entre les deux, nous avons tous nos includes, typedefs et prototypes de fonctions. J'ai quelques questions, pour voir si je comprends bien:

  1. Si j'ai un fichier c++ A. HH qui inclut un fichier D'en-têteC B. h, inclut un autre fichier D'en-têteC C. h, comment cela fonctionne? Je pense que lorsque le compilateur étapes dans B. h, {[3] } sera défini, donc il sera enveloppez le code avec extern "C" (et __cplusplus ne seront pas définie à l'intérieur de ce bloc). Si, quand il marche dans C. h, __cplusplus ne sera pas défini et le code ne sera pas enveloppé dans extern "C". Est-ce correct?

  2. Y a-t-il un problème avec envelopper un morceau de code avec extern "C" { extern "C" { .. } }? Quelle sera la seconde extern "C" faire?

  3. Nous ne mettons pas cette enveloppe autour de la .fichiers c, juste le .h fichiers. Alors, que se passe-t-il si une fonction n'a pas de prototype? Le compilateur pense que c'est une Fonction c++?

  4. Nous utilisons également des tiers code qui est écrit en C , et qui ne pas avoir ce genre d'emballage autour il. Chaque fois que j'inclus un en-tête à partir de cette bibliothèque, j'ai été de mettre un extern "C" autour du # include. Est-ce la bonne façon de traiter avec qui?

  5. Enfin, est-ce une bonne idée? Est-il autre chose que nous devrions faire? Nous allons mélanger C et C++ pour l'avenir prévisible, et je voulez vous assurer que nous allons couvrir tous les nos bases.

250
demandé sur Jonathan Leffler 2010-09-24 21:03:28

3 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é comme C, si elle est dans un .fichier cpp, il sera compilé en C++ (sauf si vous faites quelque chose d'étrange à votre configuration).

Ce que extern "C" fait affecte le lien. Les fonctions c++, lorsqu'elles sont compilées, ont leurs noms mutilés - c'est ce qui rend la surcharge possible. Le nom de la fonction est modifié en fonction des types et du nombre de paramètres, de sorte que deux fonctions avec le même nom aura 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 sur ce que vous pouvez faire dans un bloc externe "C", mais ils sont tous sur le lien. Vous ne pouvez pas définir de nouveaux symboles qui ne peuvent pas être construits avec la liaison C. Cela signifie pas de classes ou de modèles, par exemple.

extern "C" blocs nid bien. Il y a aussi extern "C++" Si vous vous trouvez désespérément coincé à l'intérieur des régions extern "C", mais ce n'est pas une bonne idée d'une propreté perspective.

Maintenant, en particulier en ce qui concerne vos questions numérotées:

En ce qui concerne #1: _ _ cplusplus doit être défini à l'intérieur des blocs extern "C". Cela n'a pas d'importance, cependant, puisque les blocs devraient nid proprement.

En ce qui concerne #2: _ _ cplusplus sera défini pour toute unité de compilation en cours d'exécution via le compilateur C++. Généralement, cela signifie que .rpc fichiers et les fichiers inclus par qui .fichier cpp. La même .h (ou .HH ou .php ou quoi avez-vous) pourrait être interprété comme C ou C++ à des moments différents, si différentes unités de compilation les incluent. Si vous voulez les prototypes dans le .h fichier pour faire référence aux noms de symboles C, alors ils doivent avoir extern "C" lorsqu'ils sont interprétés comme C++, et ils ne devraient pas avoir extern "C" lorsqu'ils sont interprétés comme C - d'où la vérification #ifdef __cplusplus.

Pour répondre à votre question # 3: les fonctions sans prototypes auront une liaison C++ si elles sont dedans .fichiers cpp et non à l'intérieur d'un extern "C" bloc. C'est bien, cependant, parce que si elle 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 ce à quoi ressemble le lien, car vous ne prévoyez pas que cette fonction soit appelée par quoi que ce soit en dehors de la même unité de compilation de toute façon.

Pour #4, vous l'avez exactement. Si vous incluez un en-tête pour le code qui a une liaison C (comme le code qui a été compilé par un compilateur C), alors vous devez extern "C" l'en-tête-de cette façon, vous serez en mesure de lier avec la bibliothèque. (Sinon, votre éditeur de liens chercherait des fonctions avec des noms comme _Z1hic lorsque vous cherchiez void h(int, char)

5: ce genre de mélange est une raison courante d'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.

223
répondu Andrew Shelansky 2010-09-24 17:32:50
  1. extern "C" ne modifie pas la présence ou l'absence de la macro __cplusplus. Il change simplement le lien et le nom-mangling des déclarations enveloppées.

  2. Vous pouvez imbriquer des blocs extern "C" assez heureusement.

  3. Si vous compilez vos fichiers .c en C++, tout ce qui n'est pas dans un bloc extern "C" et sans prototype extern "C" sera traité comme une fonction C++. Si vous les compilez en tant que C alors bien sûr tout sera un C fonction.

  4. Oui

  5. Vous pouvez mélanger en toute sécurité C et c++ de cette façon.

33
répondu Anthony Williams 2015-11-27 11:02:52

Quelques pièges qui sont colloraires à L'excellente réponse D'Andrew Shelansky et être un peu en désaccord 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 surcharger les mêmes noms de fonctions avec des paramètres différents-c'est l'une des principales caractéristiques de la modification des noms du compilateur. Il est décrit comme un problème de liaison, mais ce n'est pas tout à fait vrai-vous obtiendrez des erreurs des deux le compilateur et l'éditeur de liens.

Les erreurs du compilateur seront si vous essayez d'utiliser des fonctionnalités c++ de la déclaration du 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 extern "C" wrapper autour des déclarations et que l'en-tête est inclus dans un mélange de sources C et c++.

Une raison pour décourager les gens d'utiliser le paramètre compile C as C++ est parce que signifie que leur code source n'est plus portable. Ce paramètre est un paramètre de projet et donc, si un .le fichier c est déposé dans un autre projet, il ne sera pas compilé en c++. Je préférerais que les gens prennent le temps de renommer les suffixes de fichiers .rpc.

16
répondu Andy Dent 2014-08-26 08:15:16