web-dev-qa-db-fra.com

Puis-je utiliser une bibliothèque partagée créée en C ++ dans un programme C?

Je crée des programmes en utilisant C. Cependant, je dois utiliser beaucoup de bibliothèques qui ont des API uniquement pour C++. Alors, est-il possible que je puisse créer un objet partagé en C++ puis accéder à ses fonctionnalités en utilisant C?

  1. Les seules données que je transmettrais et retournerais seraient des types de données compatibles C.
  2. La conversion ou la migration vers cpp n'est pas une option ici.

S'il n'est pas possible d'interfacer ces codes, comment puis-je obtenir des informations du code C++ au code C? J'ai essayé d'appeler des fonctions C++ à partir de C, mais j'obtiens des erreurs lors de la liaison lorsque j'inclus <string>. Donc, lorsque j'appelle des fonctions C++ à partir de C, dois-je utiliser uniquement ce code qui sera compatible avec le compilateur C?

En-tête C++ cppfile.hpp

#ifndef CPPFILE_H
#define CPPFILE_H
    #ifdef __cplusplus
    extern "C" {
    #endif

    extern int myfunction(const char *filename);

   #ifdef __cplusplus
   }
   #endif
#endif

Fichier C++ cppfile.cpp

#include "cppfile.hpp"
#include <string>
int myfunction(const char *filename) {
    String S(filename);
    return 0;
}

Fichier C cmain.c

#include "cppfile.hpp"
int main(int argc, char **argv)
{
     int i = myfunction(argv[1]);
     printf("%d\n", i);
     return 0;
}

Compilation:

gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
30
kicker86

Vous voulez quelque chose de plus comme ça (et ici je vais utiliser un exemple un peu plus significatif):

En-tête C/C++ - animal.h

#ifndef ANIMAL_H
#define ANIMAL_H

#ifdef __cplusplus
class Animal {
public:
    Animal() : age(0), height(0) {}
    Animal(int age, float height) : age(age), height(height) {}
    virtual ~Animal() {}

    int   getAge();
    void  setAge(int new_age);

    float getHeight();
    void  setHeight(float new_height);

private:
    int age;
    float height; // in metres!
};
#endif /* __cplusplus */

#ifdef __cplusplus
extern "C" {
#endif
    struct animal; // a Nice opaque type

    struct animal *animal_create();
    struct animal *animal_create_init(int age, float height);
    void           animal_destroy(struct animal *a);

    void           animal_setage(struct animal *a, int new_age);
    void           animal_setheight(struct animal *a, float new_height);
    int            animal_getage(struct animal *a);
    float          animal_getheight(struct animal *a);
#ifdef __cplusplus
}
#endif

#endif /* ANIMAL_H */

Fichier d'implémentation animale C++ - animal.cpp

#include "animal.h"
#define TO_CPP(a) (reinterpret_cast<Animal*>(a))
#define TO_C(a)   (reinterpret_cast<animal*>(a))

void  Animal::setAge(int new_age) { this->age = new_age; }
int   Animal::getAge() { return this->age; }
void  Animal::setHeight(float new_height) { this->height = new_height; }
float Animal::getHeight() { return this->height; }

animal *animal_create() {
    animal *a = TO_C(new Animal);
    return a;
}

animal *animal_create_init(int age, float height) {
    animal *a = TO_C(new Animal(age, height));
    return a;
}

void animal_destroy(animal *a) {
    delete TO_CPP(a);
}

void animal_setage(animal *a, int new_age) {
    TO_CPP(a)->setAge(new_age);
}

void animal_setheight(animal *a, float new_height) {
    TO_CPP(a)->setHeight(new_height);
}

int animal_getage(animal *a) {
    TO_CPP(a)->getAge();
}

float animal_getheight(animal *a) {
    TO_CPP(a)->getHeight();
}

Code client C - main.c

#include "animal.h"
#include <stdio.h>

int main()
{
    // 6'0" 25yo (perhaps a human? :P)
    struct animal *a = animal_create(25, 1.83); 

    animal_setage(a, 26); // birthday
    printf("Age: %d\nHeight: %f", animal_getage(a), animal_getheight(a));

    animal_destroy(a);
    return 0;
}

Code client C++ - main.cpp

#include "animal.h"
#include <iostream>

int main()
{
    // 6'0" 25yo (perhaps a human? :P)
    Animal* a = new Animal(25, 1.83);
    a->setAge(26); // birthday
    std::cout << "Age:    " << a->getAge() << std::endl;
    std::cout << "Height: " << a->getHeight();

    delete a;
    return 0;
}

Ainsi, lorsque vous compilez la bibliothèque, vous compilez animal.cpp avec un compilateur C++. Vous pouvez ensuite le lier avec le code C et utiliser le animal_xxx les fonctions.

Notez l'utilisation de struct animal et Animal. Animal est un type C++ normal. C'est exactement à quoi ça ressemble. struct animal, en revanche, est de type "opaque". Cela signifie que votre programme C peut voir qu'il est là, et peut en avoir un, mais il ne sait pas ce qu'il contient. Tout ce qu'il sait, c'est qu'il a une fonction qui prend un struct animal*.

Dans une vraie bibliothèque, vous voudrez avoir des points de personnalisation pour l'allocation de mémoire. Donc, en supposant qu'il s'agit de la bibliothèque libjungle, vous voulez probablement au moins jungle_setmalloc et jungle_setfree avec des valeurs par défaut sensibles. Vous pouvez ensuite configurer les global new et delete dans le code C++ de libjungle pour utiliser ces fonctions définies par l'utilisateur.

28
Miles Rout

C'est tout à fait possible. Voici comment, rapidement: 1.) Vous avez un header.h avec une API C qui n'inclut aucun Cplusiness.

#ifndef MIXEDCCPP_H
#define MIXEDCCPP_H

#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h> // Any C-compatible headers will go here.

// C API goes here.  C Functions can't contain any CPPiness.
void myclass_setName( void *pClassObj, const char *pName, int nameLen );

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus

// Stuff that is only compatible with CPP goes here
// __cplusplus section won't get processed while compiling C files.

#include <vector> // CPP headers.


class MyClass {
   // Classes etc.
};
#endif // #ifdef __cplusplus

#endif // MIXEDCCPP_H

Ensuite, dans le .cpp, vous créez simplement des fonctions C-API qui peuvent même inclure CPP directement:

#include "mixedccpp.h"

extern "C" {
// C API goes here.  C Functions can't contain any CPPiness in their prototypes.
void myclass_setName( void *pClassObj, const char *pName, int nameLen )
{
    // But CPP knowledge can go inside the function - no problem, since this is a CPP file.
    MyClass *pMyClass = static_cast<MyClass *>(pClassObj);
    pMyClass->setName( pName, nameLen );
}

} // #extern "C"


// CPP Stuff goes here... or vice-versa.

Dans votre cas, vous n'avez en fait besoin d'aucun code CPP déclaré dans votre en-tête puisque vous appelez des bibliothèques externes. Mais vous devez créer des fonctions compatibles C dans votre fichier CPP qui peuvent appeler les bibliothèques CPP. Utilisez extern "C" pour les fonctions qui doivent être appelées à partir de fichiers C, puis utilisez des structures C au lieu de classes et, si des classes sont nécessaires, utilisez void * pour les pointer vers elles, puis les restituer à leur classe à partir de la classe Fonction C à tout moment pour y accéder. Un makefile standard devrait être en mesure de compiler cela très bien, en supposant qu'il compile les fichiers .cpp en tant que .cpp et comprend extern "C" {}

10
c.fogelklou

Votre code C ne peut pas utiliser l'en-tête C++ <string>. Vous devez vous assurer que les fonctions de l'API C++ qui doivent être appelées à partir de C sont déclarées extern "C" (Comme vous l'avez) et n'utilisez que des types reconnus par un compilateur C (comme vous l'avez).

Vous devez également établir une liaison avec le compilateur C++ si l'un de vos codes est en C++. Vous pouvez le faire autrement si vous êtes prêt à dépenser beaucoup d'énergie pour obtenir les bonnes options du chargeur, mais il est beaucoup plus simple d'utiliser le compilateur C++:

gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
g++ -o cmain cmain.o cppfile.so

Bien sûr, vous devez:

  1. Ajoutez #include <stdio.h> Dans cmain.c.
  2. Utilisez std::string S(filename); dans cppfile.cpp.

De plus, si le programme est invoqué sans arguments, vous obtenez:

$ ./cmain
terminate called throwing an exceptionAbort trap: 6
$ ./cmain x3
0
$

Vous devez vous protéger contre les abus, même dans les programmes de test.

4
Jonathan Leffler