Utilisation de la bibliothèque C ++ dans le code C

103

J'ai une bibliothèque C ++ qui fournit diverses classes pour gérer les données. J'ai le code source de la bibliothèque.

Je souhaite étendre l'API C ++ pour prendre en charge les appels de fonction C afin que la bibliothèque puisse être utilisée avec le code C et le code C ++ en même temps.

J'utilise la chaîne d'outils GNU (gcc, glibc, etc.), donc la prise en charge du langage et de l'architecture n'est pas un problème.

Y a-t-il des raisons pour lesquelles cela n'est techniquement pas possible?

Y a-t-il des problèmes auxquels je dois faire attention?

Existe-t-il des ressources, des exemples de code et / ou de la documentation disponibles à ce sujet?


Quelques autres choses que j'ai découvertes:

  1. Utilisez ce qui suit pour encapsuler vos en-têtes C ++ qui doivent être utilisés par le code C.

#ifdef __cplusplus
extern "C" {  
#endif  
//  
// Code goes here ...  
//  
#ifdef __cplusplus  
} // extern "C"  
#endif
  1. Conservez les "vraies" interfaces C ++ dans des fichiers d'en-tête séparés qui ne sont pas inclus par C. Pensez au principe PIMPL ici. Utiliser des #ifndef __cplusplus #errortrucs aide ici à détecter toute folie.
  2. Attention aux identifiants C ++ en tant que noms dans le code C
  3. Enums variant en taille entre les compilateurs C et C ++. Probablement pas un problème si vous utilisez la chaîne d'outils GNU, mais soyez prudent.
  4. Pour les structures, suivez la forme suivante afin que C ne soit pas confus.

    typedef struct X { ... } X
  5. Ensuite, utilisez des pointeurs pour faire passer les objets C ++, il suffit de les déclarer en C comme struct X où X est l'objet C ++.

Tout cela est une gracieuseté d'un ami qui est un assistant en C ++.

Misha M
la source
5
Un peu tard, mais j'ai écrit un petit howto sur le wrapper C pour C ++: teddy.ch/c++_library_in_c
Teddy

Réponses:

69

Oui, c'est certainement possible. Vous devrez écrire une couche d'interface en C ++ qui déclare des fonctions avec extern "C":

extern "C" int foo(char *bar)
{
    return realFoo(std::string(bar));
}

Ensuite, vous appellerez foo()depuis votre module C, qui passera l'appel à la realFoo()fonction implémentée en C ++.

Si vous avez besoin d'exposer une classe C ++ complète avec des membres de données et des méthodes, vous devrez peut-être faire plus de travail que cet exemple de fonction simple.

Greg Hewgill
la source
Doit extern "C"être placé uniquement dans les déclarations (et non dans les définitions)? Parce que vous avez mentionné «la couche qui déclare les fonctions», mais votre exemple de code est également une définition. En d'autres termes, devrions-nous le placer dans des fichiers d'en-tête ou des fichiers source? (Ou les deux?)
kyriakosSt
@KyrSt: Si vous avez un fichier d'en-tête avec une déclaration de fonction, vous devez au moins extern "C"y mettre . Votre compilateur vous dira si vous devez également le mettre sur la définition.
Greg Hewgill
23

FAQ C ++ Lite: "Comment mélanger du code C et C ++" .

Certains pièges sont décrits dans les réponses à ces questions:

  • [32.8] Comment puis-je passer un objet d'une classe C ++ vers / depuis une fonction C?
  • [32.9] Ma fonction C peut-elle accéder directement aux données d'un objet d'une classe C ++?
Alex B
la source
12

Main gotcha: les exceptions ne peuvent pas être interceptées en C. S'il y a la possibilité qu'une exception se produise dans le code C ++, écrivez votre code C ou vos wrappers C ++ très soigneusement. Inversement, des mécanismes de type exception (ie, longjump) dans le code C (comme on le trouve dans divers langages de script) ne sont pas nécessaires pour appeler des destructeurs pour les objets C ++ sur la pile.

ejgottl
la source
2
Excellent point sur les appels longjump. Bien que je ne les utilise pas directement, les frameworks de test que j'utilise les implémentent. Quelque chose à garder à l'esprit. Merci
Misha M
3

vous pouvez mélanger du code C / C ++. Si votre fonction main () est en C ++, il vous suffit de vous assurer que vos fonctions c sont déclarées

extern "C"

Si votre principal est C, alors vous êtes probablement OK sauf pour les variables statiques. Tous les constructeurs avec vos variables statiques sont censés être appelés avant le démarrage de main (). Cela n'arrivera pas si C est votre principal. Si vous avez beaucoup de variables statiques, la meilleure chose à faire est de remplacer les variables statiques par des singletons.

David Nehme
la source