Как вызвать функцию C++ из C?
Я знаю это.
Вызов функции C из C++:
Если бы мое приложение было на C++, и мне нужно было вызывать функции из библиотеки, написанной на C. Тогда я бы использовал
//main.cpp
extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.
Это не испортит имя C_library_function
и компоновщик найдет то же имя во входных файлах *.lib, и проблема решена.
Вызов функции C++ из C???
Но здесь я расширяю большое приложение, написанное на C, и мне нужно использовать библиотеку, написанную на C++. Искаженное имя в C++ вызывает здесь проблемы. Линкер жалуется на неразрешенные символы. Ну, я не могу использовать компилятор C++ над моим проектом C, потому что это ломает много других вещей. Какой выход?
Кстати я использую MSVC
6 ответов
Вам нужно создать C API для демонстрации функциональности вашего кода C++. По сути, вам нужно будет написать код C++, который объявлен extern "C" и имеет чистый API C (например, не использующий классы), который оборачивает библиотеку C++. Затем вы используете созданную вами чистую библиотеку C.
Ваш C API может опционально следовать объектно-ориентированному стилю, даже если C не является объектно-ориентированным. Пример:
// *.h file
// ...
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
typedef void* mylibrary_mytype_t;
EXTERNC mylibrary_mytype_t mylibrary_mytype_init();
EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype);
EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param);
#undef EXTERNC
// ...
// *.cpp file
mylibrary_mytype_t mylibrary_mytype_init() {
return new MyType;
}
void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) {
MyType* typed_ptr = static_cast<MyType*>(untyped_ptr);
delete typed_ptr;
}
void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) {
MyType* typed_self = static_cast<MyType*>(untyped_self);
typed_self->doIt(param);
}
Я бы сделал это следующим образом:
(При работе с MSVC игнорируйте команды компиляции GCC)
Предположим, что у меня есть класс C++ с именем AAA, определенный в файлах aaa.h, aaa.cpp, и что у класса AAA есть метод с именем sayHi (const char * name), который я хочу включить для кода C.
Код C++ класса AAA - чистый C++, я не изменяю его:
// aaa.h
#ifndef AAA_H
#define AAA_H
class AAA {
public:
AAA();
void sayHi(const char *name);
};
#endif
// aaa.cpp
#include <iostream>
#include "aaa.h"
AAA::AAA() {
}
void AAA::sayHi(const char *name) {
std::cout << "Hi " << name << std::endl;
}
Компиляция этого класса, как это обычно делается для C++. Этот код "не знает", что он будет использоваться кодом Си. Используя команду:
g++ -fpic -shared aaa.cpp -o libaaa.so
Теперь, также в C++, создание коннектора C. Определяем его в файлах aaa_c_connector.h, aaa_c_connector.cpp. Этот соединитель будет определять функцию C с именем AAA_sayHi (cosnt char * name), которая будет использовать экземпляр AAA и будет вызывать его метод:
// aaa_c_connector.h
#ifndef AAA_C_CONNECTOR_H
#define AAA_C_CONNECTOR_H
#ifdef __cplusplus
extern "C" {
#endif
void AAA_sayHi(const char *name);
#ifdef __cplusplus
}
#endif
#endif
// aaa_c_connector.cpp
#include <cstdlib>
#include "aaa_c_connector.h"
#include "aaa.h"
#ifdef __cplusplus
extern "C" {
#endif
// Inside this "extern C" block, I can define C functions that are able to call C++ code
static AAA *AAA_instance = NULL;
void lazyAAA() {
if (AAA_instance == NULL) {
AAA_instance = new AAA();
}
}
void AAA_sayHi(const char *name) {
lazyAAA();
AAA_instance->sayHi(name);
}
#ifdef __cplusplus
}
#endif
Снова скомпилируйте его, используя обычную команду C++:
g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so
Теперь у меня есть общая библиотека (libaaa_c_connector.so), которая реализует функцию C AAA_sayHi (const char * name). Теперь я могу создать главный файл C и собрать его все вместе:
// main.c
#include "aaa_c_connector.h"
int main() {
AAA_sayHi("David");
AAA_sayHi("James");
return 0;
}
Компиляция с помощью команды компиляции C:
gcc main.c -L. -laaa_c_connector -o c_aaa
Мне нужно будет установить LD_LIBRARY_PATH, чтобы он содержал $PWD, и если я запустите исполняемый файл ./c_aaa, я получу ожидаемый вывод:
Hi David
Hi James
РЕДАКТИРОВАТЬ:
В некоторых дистрибутивах Linux, -laaa
а также -lstdc++
может также потребоваться для последней команды компиляции. Благодаря @AlaaM. за внимание
Предполагая, что C++ API совместим с C (без классов, шаблонов и т. Д.), Вы можете обернуть его в extern "C" { ... }
так же, как вы сделали, когда шли другим путем.
Если вы хотите выставлять объекты и другие симпатичные C++ вещи, вам придется написать API-оболочку.
Вам придется написать оболочку для C на C++, если вы хотите это сделать. C++ имеет обратную совместимость, но C не поддерживает прямую совместимость.
Экспортируйте ваши функции C++ как extern "C" (или символы стиля C), или используйте формат файла.def, чтобы определить недекорированные символы экспорта для компоновщика C++ при создании библиотеки C++, тогда компоновщик C не должен иметь проблем при чтении
#include <iostream>
//////////////
// C++ code //
//////////////
struct A
{
int i;
int j;
A() {i=1; j=2; std::cout << "class A created\n";}
void dump() {std::cout << "class A dumped: " << i << ":" << j << std::endl;}
~A() {std::cout << "class A destroyed\n";}
};
extern "C" {
// this is the C code interface to the class A
static void *createA (void)
{
// create a handle to the A class
return (void *)(new A);
}
static void dumpA (void *thisPtr)
{
// call A->dump ()
if (thisPtr != NULL) // I'm an anal retentive programmer
{
A *classPtr = static_cast<A *>(thisPtr);
classPtr->dump ();
}
}
static void *deleteA (void *thisPtr)
{
// destroy the A class
if (thisPtr != NULL)
{
delete (static_cast<A *>(thisPtr));
}
}
}
////////////////////////////////////
// this can be compiled as C code //
////////////////////////////////////
int main (int argc, char **argv)
{
void *handle = createA();
dumpA (handle);
deleteA (handle);
return 0;
}
Вы можете префикс объявления функции с помощью ключевого слова extern "C", например
extern â € œCâ € int Mycppfunction ()
{
// Код идет сюда
возврат 0;
}
Дополнительные примеры вы можете найти в Google по ключевому слову "внешнее". Вам нужно сделать еще несколько вещей, но это несложно, вы получите много примеров от Google.