Czy Mogę używać biblioteki współdzielonej utworzonej w C++ w programie C?

Tworzę programy w C. wymagam jednak korzystania z wielu bibliotek, które mają API tylko dla C++. Czy jest więc możliwe, że mogę utworzyć obiekt współdzielony w C++, a następnie uzyskać dostęp do jego funkcjonalności za pomocą C?

  1. jedyne dane, które przekazywałbym i zwracałbym, to typy danych kompatybilne z C.
  2. Konwersja lub migracja do cpp nie jest tutaj możliwa.

Jeśli nie jest możliwe połączenie tych kodów, jak uzyskać informacje z kodu C++ do kodu C? I próbowałem wywołać funkcje C++ z C, ale dostaję błędy podczas łączenia, gdy dołączam <string>. Więc kiedy wywołuję funkcje C++ z C, czy powinienem używać tylko tego kodu, który będzie kompatybilny z kompilatorem C?

C++ header cppfile.hpp

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

    extern int myfunction(const char *filename);

   #ifdef __cplusplus
   }
   #endif
#endif

C++ plik cppfile.cpp

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

C plik cmain.c

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

Kompilacja:

gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
Author: Jonathan Leffler, 2013-02-17

3 answers

Chcesz czegoś bardziej podobnego (a tutaj użyję nieco bardziej sensownego przykładu):

Nagłówek 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 */

Plik implementacji C++ animal-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();
}

Kod klienta 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;
}

C++ client code - 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;
}

Więc kiedy kompilujesz bibliotekę, kompilujesz animal.cpp za pomocą kompilatora C++. Następnie możesz połączyć się z nim za pomocą kodu C i użyć funkcji animal_xxx.

Zwróć uwagę na użycie struct animal i Animal. {[7] } jest normalnym typem C++. To jest dokładnie to, na co wygląda. struct animal, z drugiej strony, jest typem "nieprzezroczystym". Oznacza to, że twój program C widzi, że tam jest i może go mieć, ale nie wie, co jest w środku. Wie tylko, że posiada funkcję, która przyjmuje struct animal*.

W prawdziwej bibliotece będziesz chciał mieć punkty dostosowywania do alokacji pamięci. Więc zakładając, że jest to biblioteka libjungle, prawdopodobnie chcesz co najmniej jungle_setmalloc i jungle_setfree z rozsądnymi wartościami domyślnymi. Następnie można ustawić w kodzie C++ new i delete, aby używać tych funkcji zdefiniowanych przez użytkownika.

 28
Author: Miles Rout,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 01:40:51

Jest to całkowicie możliwe. Oto jak, szybko: 1.) Masz nagłówek.h z C API, które nie zawiera żadnego 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

Następnie w .cpp, po prostu tworzysz funkcje C-API, które mogą nawet zawierać CPP bezpośrednio w nich:

#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.

W Twoim przypadku, nie potrzebujesz kodu CPP zadeklarowanego w nagłówku, ponieważ wywołujesz zewnętrzne biblioteki. Ale musisz utworzyć funkcje zgodne z C w pliku CPP, które mogą wywoływać biblioteki CPP. Use extern " C" dla tych funkcji, które muszą być wywołane z plików C, a następnie użyć C-structs zamiast klas i, jeśli klasy są potrzebne, użyj void *, aby wskazać na nie, a następnie wrzucić je z powrotem do ich klasy z funkcji C w dowolnym momencie, gdy trzeba uzyskać do nich dostęp. Standardowy plik makefile powinien być w stanie skompilować to dobrze, zakładając, że kompiluje .pliki cpp jako .cpp i rozumie extern" C " {}

 9
Author: c.fogelklou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-24 22:25:42

Twój kod C nie może używać nagłówka C++ <string>. Musisz upewnić się, że funkcje w C++ API, które mają być wywołane z C, są zadeklarowane extern "C" (tak jak ty) i używają tylko typów rozpoznawanych przez kompilator C (tak jak ty).

Musisz również połączyć się z kompilatorem C++, jeśli którykolwiek z kodu jest w C++. Możesz to zrobić inaczej, jeśli jesteś gotowy poświęcić dużo energii na poprawienie opcji loadera, ale o wiele prostsze jest użycie kompilatora C++: {]}

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

Z oczywiście, musisz:

  1. dodaj #include <stdio.h> w cmain.c.
  2. użyj std::string S(filename); W cppfile.cpp.

Ponadto, jeśli program jest wywoływany bez argumentów, otrzymujemy:

$ ./cmain
terminate called throwing an exceptionAbort trap: 6
$ ./cmain x3
0
$
Musisz chronić się przed niewłaściwym użyciem, nawet w programach testowych.
 4
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-17 04:47:32