Могу ли я использовать общую библиотеку, созданную на С++ в программе на 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
Ответы
Ответ 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
$
Вам необходимо защитить от неправильного использования даже в тестовых программах.