web-dev-qa-db-fra.com

À quels problèmes puis-je m'attendre pour compiler du code C avec un compilateur C ++?

Si vous prenez une base de code C existante et la compilez avec un compilateur C++, quel genre de problèmes pouvez-vous vous attendre à rencontrer? Par exemple, je pense que l'attribution d'un entier à une valeur avec un type énuméré échouera en C++, alors que c'est légal (si un peu méchant) en C.

Si je n'emballe pas tous mes fichiers C dans extern C { ... }, est-ce que je vais me faire malmener là où je l'attends le moins? Y a-t-il une raison pour laquelle je ne devrais vraiment pas faire ça?

Pour le fond, nous avons une très grande base de code écrite en C. Depuis quelques années, nous sautons dans des cerceaux pour faire des choses qui viendraient naturellement via C++ (l'héritage homebrewe, par exemple). Nous aimerions commencer à évoluer vers le C++, mais de manière progressive; obtenir notre infrastructure de type CORBA pour le prendre en charge et refactoriser les modules au fur et à mesure pour tirer parti de l'approche plus naturelle que C++ fournirait.

35
Chris Arguin

J'ai fait quelque chose comme ça une fois. La principale source de problèmes était que C++ est plus strict sur les types, comme vous le soupçonniez. Vous devrez ajouter des transtypages où void * est mélangé avec des pointeurs d'autres types. Comme l'allocation de mémoire:

Foo *foo;
foo = malloc(sizeof(*foo));

Ce qui précède est du code C typique, mais il aura besoin d'un cast en C++:

Foo *foo;
foo = (Foo*)malloc(sizeof(*foo));

Il existe de nouveaux mots réservés en C++, tels que "classe", "et", "bool", "catch", "delete", "explicit", "mutable", "namespace", "new", "operator", "ou", "privé", "protégé", "ami", etc. Ceux-ci ne peuvent pas être utilisés comme noms de variables, par exemple.

Les problèmes ci-dessus sont probablement les problèmes les plus courants lorsque vous compilez l'ancien code C avec un compilateur C++. Pour une liste complète des incompatibilités, voir Incompatibilités entre ISO C et ISO C++ .

Vous posez également des questions sur le changement de nom. En l'absence d'encapsuleurs "C" externes, le compilateur C++ va modifier les symboles. Ce n'est pas un problème tant que vous utilisez uniquement un compilateur C++, et ne comptez pas sur dlsym () ou quelque chose comme ça pour extraire des symboles des bibliothèques .

34
Ville Laurikari

Voir Incompatibilités entre ISO C et ISO C++ pour une liste très très détaillée de toutes les incompatibilités. Il y a beaucoup de problèmes subtils, y compris certains qui ne se manifestent pas immédiatement dans une erreur de compilation. Par exemple, un problème qui peut être un problème est la taille des constantes de caractères:

// In C, prints 4.  In C++, prints 1
printf("%d\n", sizeof('A'));
23
Adam Rosenfield

Si je n'encapsule pas tous mes fichiers C dans "extern C {...}", vais-je obtenir un changement de nom là où je m'y attend le moins?

Il vous mord lorsque vous essayez de lier ensemble C et C++.

J'ai écrit beaucoup de fichiers d'en-tête contenant:

#ifdef __cplusplus
    extern "C" {
#endif

// rest of file

#ifdef __cplusplus
    }
#endif

Après un certain temps, il fusionne dans le passe-partout multiple existant et vous arrêtez de le voir. Mais vous devez faire attention où vous le mettez - généralement il appartient après tout inclut votre en-tête.

Y a-t-il une raison pour laquelle je ne devrais vraiment pas faire ça?

Si vous savez avec certitude que vous n'allez pas combiner C et C++, il n'y a aucune raison de le faire à ma connaissance. Mais avec la migration progressive que vous décrivez, il est essentiel pour tout ce qui a une interface publiée que les composants C et les composants C++ doivent utiliser.

La grande raison pas de le faire est que cela vous empêche de surcharger les fonctions (au moins, dans ces en-têtes). Vous pourriez trouver que vous voulez le faire une fois que vous avez migré tout votre code vers C++ et commencé à le maintenir/refactoriser/étendre.

8
Steve Jessop

En général, vous n'aurez aucun problème. Oui, il y a des incompatibilités entre C et C++, mais elles ne semblent pas apparaître aussi souvent que le casting malloc mentionné ci-dessus, qui est assez trivial à corriger.

J'ai compilé et utilisé avec succès les bibliothèques C open-source suivantes en C++:

  • l'analyseur XML Expat
  • le trameur de polices FreeType2
  • libjpeg: gère les images JPEG
  • libpng: gère les images PNG
  • la bibliothèque de compression Zlib

La partie la plus difficile a été l'ajout de wrappers d'espace de noms, ce qui a pris quelques heures, en grande partie à cause des instructions #include enfouies profondément dans le code, qui devaient être en dehors de l'espace de noms C++.

Pourquoi ai-je fait ça? Parce que je vends une bibliothèque commerciale que les gens liaient directement à leurs applications; et parfois, leurs applications étaient liées à d'autres versions d'Expat, de FreeType, etc. Cela provoquait des erreurs de symboles à multiplication définie. La chose la plus propre à faire était de tout déplacer dans ma bibliothèque et de le cacher dans mon espace de noms.

Cependant, je ne l'ai pas fait avec toutes les bibliothèques open-source que j'utilise. Certains n'ont pas encore provoqué de conflits, et je n'ai pas encore réussi à les résoudre, ce qui, bien que sans problème, est assez fastidieux. L'exception intéressante est SQLite, que je n'ai pas pu compiler en C++. J'ai donc fait une recherche et un remplacement massifs, en ajoutant un préfixe (le nom de mon produit) à chaque symbole visible de l'extérieur. Cela a résolu le problème de mon client.

3
Graham Asher

Autre exemple: il n'y a pas de conversion implicite des entiers en énumérations en C++, alors qu'il y en a une en C. Vous aurez besoin d'un transtypage si vous voulez vraiment le faire en C++.

3
piotr

Je l'ai fait avant d'utiliser MSVC, si l'utilisation de MSVC est une bonne stratégie:

  1. Définissez des fichiers individuels à créer en tant que CPP, de cette façon, vous pouvez passer progressivement à un compilateur CPP.
  2. Travaillez fichier par fichier en utilisant ctrl + f7 juste pour créer ce fichier.
  3. Plutôt que de diffuser tous les mallocs, vous pouvez créer une version de modèle à la place

foo = (Foo *) malloc (taille de (* foo));

devient

foo = malloc<Foo>();

Et bien sûr, vous pouvez avoir une surcharge pour les cas où vous voulez un Foo + n octets

Je recommande également de changer les allocations de mémoire pour utiliser RAII dans la mesure du possible, j'ai trouvé que certaines fonctions étaient assez complexes, donc passer à RAII était trop risqué, pour la plupart des cas, c'était assez simple à faire.

2
paulm

C++ a une vérification de type plus stricte, vous devrez donc peut-être ajouter un cast à chaque appel à malloc/realloc/calloc.

1
user253751

essayez de compiler avec un compilateur C++:

typedef enum{ false = 0, true = 1} bool;
0
Dimitrie D.