Как вызвать функцию C ++ из C?


84

Я знаю это.

Вызов функции 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



Когда вы управляете библиотекой C ++: stackoverflow.com/questions/12615683/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
Возможный дубликат Elegantly call C ++ от C
user2284570

Ответы:


95

Вам необходимо создать C API для демонстрации функциональности вашего кода C ++. По сути, вам нужно будет написать код C ++, который объявлен как extern «C» и имеет чистый C API (например, без использования классов), который охватывает библиотеку 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);
 }


1
Как связать окончательный исполняемый файл? Вы используете C или C ++? (Когда я использовал C, компоновщик не смог найти функцию C ++; когда я использовал C ++, компоновщик не смог найти std :: cout.)
Зак

1
@Zack, если вы создаете статическую библиотеку с компилятором / компоновщиком C ++, которая транзитивно включает его зависимости (включая стандартную библиотеку C ++, которая может быть неявно связана вашим компоновщиком), вы должны иметь возможность связать эту статическую библиотеку с кодом C с помощью Компилятор / компоновщик C.
Майкл Аарон Сафян

Можете ли вы экспортировать шаблон из C ++ в C?
MarcusJ

У меня «гибридное» поведение, поскольку мне нужно, чтобы моя библиотека вызывалась как кодом C ++, так и кодом C. В *.cpp fileI также необходимо , чтобы обернуть функции в #ifdef _cplusplus+ , extern "C"чтобы избежать «неопределенной ссылки на» ошибку во время соединения.
Coconop

43

Я бы сделал это так:

(При работе с MSVC игнорируйте команды компиляции GCC)

Предположим, что у меня есть класс C ++ с именем AAA , определенный в файлах aaa.h, aaa.cpp , и что у класса AAA есть метод с именем sayHi (const char * name) , который я хочу включить для кода C.

Код C ++ класса AAA - Pure 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 ++. Этот код «не знает», что он будет использоваться кодом 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 implement functions in C++, which will externally 
//   appear as C functions (which means that the function IDs will be their names, unlike
//   the regular C++ behavior, which allows defining multiple functions with the same name
//   (overloading) and hence uses function signature hashing to enforce unique IDs),


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. для внимания


1
вы также можете указать путь ссылки lib с помощьюgcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
Metaphox

что usecpp.c?
Alaa M.

1
Последний сборник линия должна быть: gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa. Обратите внимание на -laaaand-lstdc+++
Alaa M.

@AlaaM. кто-то отредактировал мой код и изменил его main.cна usecpp.c. Я только что отменил его .... Что касается другого комментария, он должен работать как есть. Используется вторая компиляция, -laaaи этого, вероятно, должно быть достаточно (я написал ее более 1,5 лет назад, я не очень хорошо помню, что я делал, но я думаю, что я тестировал каждую строку перед публикацией)
SomethingSomething

1
Хорошо, добавил это как заметку в конце поста. Спасибо за внимание!
SomethingSomething

6

Вам придется написать оболочку для C на C ++, если вы хотите это сделать. C ++ имеет обратную совместимость, но C не поддерживает прямую совместимость.


5

Предполагая, что C ++ API совместим с C (без классов, шаблонов и т. Д.), Вы можете обернуть его в extern "C" { ... } же, как вы это сделали, когда пошли другим путем.

Если вы хотите раскрыть объекты и другие милые вещи C ++, вам придется написать API-оболочку.


Не совсем ... библиотеку C ++ придется перекомпилировать.
Майкл Аарон Сафян

О нет. C ++ API полностью объектно-ориентирован.
claws

2
@claws, см. мою статью о создании кода C в стиле ООП ... и создайте библиотеку-оболочку с использованием этого стиля с интерфейсом C, но с базовой реализацией C ++. Затем сделайте ссылку на интерфейс C.
Майкл Аарон Сафян

Возможно, вы захотите взглянуть на инструменты оболочки, такие как swig, pyboost, ... которые делают аналогичные вещи (но не в отношении C ...)
xtofl

2

экспортируйте свои функции C ++ как extern «C» (также известные как символы стиля C) или используйте формат файла .def для определения недекорированных символов экспорта для компоновщика C ++ при создании библиотеки C ++, тогда компоновщик C не должен иметь проблем при чтении


0
#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;
}

2
Пожалуйста, рассмотрите возможность добавления пояснений к своим ответам.
HMD

Краткое объяснение того, как приведенный выше ответ на вопрос делает ответ более полезным для других.
SOS

-3

Вы можете префикс объявления функции с помощью ключевого слова extern «C», например

extern «C» int Mycppfunction ()

{

// Код идет сюда

возврат 0;

}

Дополнительные примеры вы можете найти в Google по ключевому слову extern. Вам нужно сделать еще несколько вещей, но это несложно, вы получите много примеров от Google.


Три отрицательных голоса и ни одного объяснения. Как всегда, отличное сообщество SO.
Zimano
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.