Подтвердить что ты не робот

Могу ли я использовать общую библиотеку, созданную на С++ в программе на C?

Я создаю программы с использованием C. Однако мне нужно использовать множество библиотек, которые имеют API только для С++. Итак, возможно ли, что я могу создать общий объект на С++, а затем получить доступ к его функциям с помощью C?

  • Единственными данными, которые я бы передавал и возвращал, были бы типы данных, совместимые с C.
  • Преобразование или переход на cpp здесь не является вариантом.

Если невозможно связать эти коды, как мне получить информацию из кода С++ в код C? Я попытался вызвать функции С++ из C, но я получаю ошибки во время связывания, когда включаю <string>. Поэтому, когда я вызываю функции С++ из C, должен ли я использовать только этот код, который будет совместим с компилятором C?

Заголовок С++ cppfile.hpp

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

    extern int myfunction(const char *filename);

   #ifdef __cplusplus
   }
   #endif
#endif

Файл С++ cppfile.cpp

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

C файл cmain.c

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

Компиляция:

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

Ответ 1

Вы хотите что-то более похожее (и здесь я буду использовать несколько более значимый пример):

Заголовок 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 */

Файл реализации животных С++ - 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();
}

Клиентский код 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;
}

Код клиента С++ - 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;
}

Поэтому, когда вы компилируете библиотеку, вы компилируете animal.cpp с помощью компилятора С++. Затем вы можете связать его с кодом C и использовать функции animal_xxx.

Обратите внимание на использование struct animal и Animal. Animal - это обычный тип С++. Это точно, как это выглядит. struct animal, с другой стороны, является "непрозрачным" типом. Это означает, что ваша программа на C может видеть ее там, и может иметь ее, но она не знает, что внутри нее. Все, что он знает, это то, что у него есть функция, которая принимает struct animal*.

В реальной библиотеке вам понадобятся точки настройки для распределения памяти. Итак, предположим, что это библиотека libjungle, вы, вероятно, хотите, по крайней мере, jungle_setmalloc и jungle_setfree с разумными значениями по умолчанию. Затем вы можете настроить глобальные new и delete в libjungle С++ код для использования этих пользовательских функций.

Ответ 2

Это вполне возможно. Вот как, быстро: 1.) У вас есть header.h с API C, который не включает 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

Затем в .cpp вы просто создаете некоторые функции C-API, которые могут даже включать в себя CPP:

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

В вашем случае вам действительно не нужен код CPP, объявленный в вашем заголовке, поскольку вы вызываете внешние библиотеки. Но вам нужно создать C-совместимые функции в вашем CPP файле, который может вызывать библиотеки CPP. Используйте extern "C" для тех функций, которые нужно вызывать из файлов C, а затем используйте C-struct вместо классов и, если нужны классы, используйте void *, чтобы указать на них, а затем отбросить их в свой класс из C в любое время, когда вам нужно получить к ним доступ. Стандартный make файл должен иметь возможность компилировать это просто отлично, предполагая, что он компилирует файлы .cpp как .cpp и понимает extern "C" {}

Ответ 3

Ваш C-код не может использовать заголовок С++ <string>. Вы должны убедиться, что функции в С++ API, которые должны быть вызваны из C, объявляются extern "C" (как и у вас) и используют только типы, распознаваемые компилятором C (как и у вас).

Вам также необходимо связать с компилятором С++, если какой-либо из ваших кодов находится на С++. Вы можете сделать это иначе, если вы готовы потратить много энергии, чтобы получить параметры загрузчика, но гораздо проще использовать компилятор С++:

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

Конечно, вам нужно:

  • Добавить #include <stdio.h> в cmain.c.
  • Используйте std::string S(filename); в cppfile.cpp.

Кроме того, если программа вызывается без аргументов, вы получаете:

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

Вам необходимо защитить от неправильного использования даже в тестовых программах.