Як викликати функцію 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




Відповіді:


95

Вам потрібно створити C API для викриття функціональності вашого коду C ++. В основному, вам потрібно буде написати код C ++, який оголошено зовнішнім словом "C" і який має чистий API 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?
MarcusJ

Я маю "гібридну" поведінку, оскільки мені потрібно, щоб мою бібліотеку викликали як за допомогою C ++, так і коду C. У програмі *.cpp fileI також потрібно було обернути функції в #ifdef _cplusplus+, extern "C"щоб уникнути помилок "невизначеного посилання на" під час посилання.
Коконоп,

41

Я б зробив це наступним чином:

(Якщо ви працюєте з MSVC, ігноруйте команди компіляції GCC)

Припустимо, що у мене є клас C ++ з іменем AAA , визначений у файлах aaa.h, aaa.cpp , і що клас AAA має метод sayHi ( ім'я const char *) , який я хочу включити для коду 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 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 *) . Тепер я можу створити основний файл 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?
Алаа М.

1
Остання збірка лінія повинна бути: gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa. Зверніть увагу на -laaaі-lstdc+++
Алаа М.

@AlaaM. хтось відредагував мій код і змінив main.cна usecpp.c. Я щойно повернув його. Щодо іншого коментаря, він також повинен працювати як є. Друга компіляція використовує, -laaaі цього, мабуть, має бути достатньо (я написав це більше 1,5 року тому, я не дуже добре пам’ятаю, що робив, але, думаю, перевірив кожен рядок перед публікацією)
SomethingSomething

1
Гаразд, додав це як примітку в кінці допису. Дякую за увагу!
SomethingSomething

6

Вам потрібно буде написати обгортку для C на C ++, якщо ви хочете це зробити. C ++ є сумісним із зворотною стороною, але C не є сумісним із переадресацією.


5

Припускаючи, що API C ++ сумісний із C (без класів, шаблонів тощо), ви можете обернути його extern "C" { ... }, як і в інший спосіб.

Якщо ви хочете виставити об'єкти та інші симпатичні речі на C ++, вам доведеться написати API обгортки.


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

О ні. API C ++ повністю об'єктно-орієнтований.
кігті

2
@claws, див. мою статтю про створення коду C у стилі OOP .. та створення бібліотеки обгортки з використанням цього стилю з інтерфейсом C, але базовою реалізацією C ++. Потім посилання на інтерфейс C.
Майкл Аарон Сафян

Можливо, ви захочете поглянути на "інструменти" обгортки, такі як swig, pyboost, ..., які роблять еквівалентні речі (але не назустріч C ...)
xtofl

2

експортуйте свої функції C ++ як зовнішній "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

Ви можете до префіксу оголошення функції додати зовнішнє ключове слово “C”, наприклад

extern “C” int Mycppfunction ()

{

// Код йде сюди

повернути 0;

}

Щоб отримати більше прикладів, ви можете більше шукати в Google про ключове слово “extern”. Вам потрібно зробити ще кілька речей, але неважко отримати багато прикладів від Google.


Три голоси проти і жодного пояснення. Будучи чудовим, як завжди, ТАКА спільнота.
Zimano
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.