Envelopper une librairie C avec extern “C” sauf une inclusion C ++ interne

J’ai une bibliothèque C que je dois utiliser dans un code C ++, je dois donc envelopper toute la bibliothèque avec un bloc extern "C" . Le problème est que la bibliothèque semble inclure un code compilé C ++. Envelopper toute la bibliothèque engloberait donc également cet en-tête C ++.

comprend

Dans lib.h je lib.h que tous les en-têtes internes que je veux exposer, quelque chose comme ceci:

 #ifndef LIB_H #define LIB_H #include "lib_foo.h" #include "lib_bar.h" #include "lib_baz.h" #endif 

Le client n’aura donc besoin d’inclure que lib.h pour utiliser lib.

Dans ma première tentative j’ai fait ceci:

 #ifndef LIB_H #define LIB_H extern "C" { #include "lib_foo.h" #include "lib_bar.h" #include "lib_baz.h" } #endif 

Mais alors, je reçois une erreur de recherche de symbole lorsque j’exécute une fonction dans already_comstackd_c++.h

Comment puis-je éviter d’appliquer extern "C" dans le fichier d’en-tête already_comstackd_c++.h ?


Modifier:

Résolu Ce n’était pas un problème avec extern "C" , c’était un problème qui liait correctement la bibliothèque compilée c ++ avec gyp: Utilisation de la bibliothèque partagée dans Gyp dans node-sqlite3

Notez que extern C n’est pas légal, il ne doit donc être inclus que dans la compilation en C ++.

L’en-tête already_comstackd_c ++. H contient probablement une protection contre les inclus multiples, donc incluez-le d’abord:

 #ifndef LIB_H #define LIB_H # This include added so that it won't get marked extern "C" when included by lob_foo.h. #include  #ifdef __cplusplus extern "C" { #endif #include "lib_foo.h" #include "lib_bar.h" #include "lib_baz.h" #ifdef __cplusplus } #endif #endif 

Remarque: Vous devez vérifier si already_comstackd_c++.h est inclus de manière conditionnelle et append les conditions correspondantes.

Vous ne pouvez pas appeler C ++ à partir d’un fichier compilé en clair C.

Si lib_foo.h est en clair C, il ne peut pas utiliser directement les fonctions de already_comstackd_c ++. H. Vous aurez probablement besoin de créer un wrapper C pour cela.

Cette réponse peut vous aider davantage: Appelez C ++ élégamment depuis C

Si votre diagramme est exact, alors lib_foo.h ne devrait pas inclure already_comstackd_c++.h lib_foo.h Modifiez-le pour arrêter d’inclure cela.

Si les fonctions déclarées dans already_comstackd_c++.h eu leur implémentation compilée pour utiliser l’ABI C ++, il n’y a aucun moyen que votre programme C puisse établir une liaison avec elles. (à court de piratage extrêmement laide).

Pour éviter de tels incidents à l’avenir, insérez explicitement #ifdef __cplusplus extern "C" { gardes dans chaque fichier d’en-tête qui aura une implémentation C, mais qui pourrait être utilisé dans un programme C ++, et inversement.

Une solution de contournement à votre situation actuelle consisterait à créer un fichier .cpp contenant des thunks pour les fonctions dont vous avez besoin. Publiez vos thunks sous extern "C" et implémentez-les en appelant les fonctions de already_comstackd_c++.h

Repensez votre conception. Vous ne pouvez pas inclure les en-têtes C ++ à partir de C, mais uniquement l’inverse. Quelques suggestions:

1) Bien que l’idée initiale derrière ‘extern “C” était de résumer les en-têtes C à partir de fichiers C ++, si vous avez des en-têtes qui devraient être accessibles en C mais qui sont implémentés en C ++, l’approche habituelle consiste à envelopper les déclarations et pas l’inclus. (Avec le #ifdef __cplusplus requirejs, le code C, qui traite toujours les en-têtes comme étant C, ne s’étouffe pas avec le C ++ inutile – ism ‘extern “C”‘)

2) N’exposez pas C ++ en tant qu’API publique d’une bibliothèque. C ++ en général ne fournit pas une ABI stable binary. Si vous ajoutez une méthode et que ce n’est pas la fin, ou si vous ajoutez une variable d’instance à une classe, ou si vous modifiez quelque chose dans une classe basée sur un modèle, tous les clients devront être recompilés (avec des bibliothèques statiques qui se produisent de toute façon, mais c’est un gros problème pour les dylibs / DLL / frameworks). Donc, en général, la meilleure idée est de conserver C ++ en tant que détail d’implémentation et d’exposer uniquement les fonctionnalités C de votre bibliothèque.

3) Si vous devez exposer C ++ à partir de votre bibliothèque, utilisez le modèle Pimpl (implémentation privée) et aucun modèle, et placez-le dans un en-tête séparé qui n’est pas inclus dans l’en-tête C principal, afin que les clients C ne puissent pas l’inclure. . Un tel en-tête séparé est également utile pour # 2 car les fichiers d’implémentation des modules de votre bibliothèque (qui sont en C ++) peuvent l’inclure et ainsi utiliser les classes qu’il contient.

4) Pour C ++, un pointeur sur une struct Foo et un pointeur sur une classe Foo sont la même chose. Ainsi, si vous devez renvoyer une classe C ++ à partir d’une API C-only implémentée dans les coulisses de C ++, vous pouvez simplement utiliser toujours struct Foo * dans les en-têtes (que C comprend correctement) et fournir des fonctions de wrapper C pour le méthodes de la classe comme:

 extern "C" int FooGetCount( struct Foo* thisFoo ) { return thisFoo->GetCount(); } 

De cette façon, ils peuvent conserver des pointeurs sur les objects C ++ et accéder à leurs propriétés, sans avoir à utiliser réellement C ++. Bien sûr, vous devez également fournir des wrappers similaires pour la création / suppression de l’object, car C n’a pas les opérateurs new / delete.