Виклик функції C із коду C ++


90

У мене є функція C, яку я хотів би викликати з C ++. Я не міг використати " extern "C" void foo()" підхід, оскільки "" функцію C не вдалося скомпілювати за допомогою g ++. Але він чудово компілюється за допомогою gcc. Будь-які ідеї, як викликати функцію з C ++?


1
Не могли б ви написати кілька прикладів коду та g++повідомлень про помилки
Matthieu Rouget

7
Якщо ви компілюєте його за допомогою компілятора C ++, це C ++. Код С не повинен компілюватися за допомогою компілятора С ++. Це різні мови. Ваш код не є дійсним на C ++ і тому не компілюється з компілятором C ++.
xaxxon

3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
аутист

2
Моя спроба: void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_

1
Це слід залишити відкритим, тим більше, що в ньому є добрі відповіді, які вказують на те, як компілятор C (а не C ++) можна використовувати для коду C.
Кріс Страттон,

Відповіді:


126

Скомпілюйте код С наступним чином:

gcc -c -o somecode.o somecode.c

Тоді код С ++ такий:

g++ -c -o othercode.o othercode.cpp

Потім зв’яжіть їх разом, за допомогою компоновника C ++:

g++ -o yourprogram somecode.o othercode.o

Ви також повинні повідомити компілятору C ++, що надходить заголовок C, коли ви включаєте декларацію для функції C. Отже, othercode.cppпочинається з:

extern "C" {
#include "somecode.h"
}

somecode.h повинен містити щось на зразок:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(Я використовував gcc у цьому прикладі, але принцип однаковий для будь-якого компілятора. Створюйте окремо як C та C ++ відповідно, а потім зв’язуйте його разом.)


7
@Arne Гарні бали. Деякі люди розбирають деякі C ++ у своїх C, загортаючи extern "C"в заголовок #ifdef __cplusplus.
розмотати

@Arne Дивіться мою відповідь нижче. розслабитися Як бачите, я один з них;)
gx_

1
Дуже дякую ! Це було мені дуже корисно :)
Hesham Eraqi

Я отримав таку помилку: Помилка: # 337: специфікація посилання несумісна з попереднім "foo" (оголошено в рядку 1) Тепер його компіляція гарна. Хтось може пояснити?
FaizanHussainRabbani

@FaizanRabbani, не без набагато детальніших деталей.
Проф. Фалькен,

61

Дозвольте мені зібрати шматочки з інших відповідей та коментарів, щоб навести вам приклад із чітко відокремленими кодами C та C ++:

Частина C:

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

void foo(void)
{
    /* ... */
}

Складіть це за допомогою gcc -c -o foo.o foo.c.

Частина C ++:

bar.cpp

extern "C" {
  #include "foo.h" //a C header, so wrap it in extern "C" 
}

void bar() {
  foo();
}

Складіть це за допомогою g++ -c -o bar.o bar.cpp

А потім зв’яжіть все це разом:

g++ -o myfoobar foo.o bar.o

Обгрунтування: Код С повинен бути простим кодом С, немає #ifdefс для "можливо колись я зателефоную це з іншої мови". Якщо якийсь програміст на C ++ викликає ваші функції C, це їх проблема, а не ваша. І якщо ви програміст на C ++, то заголовок C може бути не вашим, і ви не повинні його змінювати, тому обробка неімонтованих імен функцій (тобто extern "C") належить до вашого коду C ++.

Ви, звичайно, можете написати собі зручний заголовок C ++, який нічого не робить, крім загортання заголовка C у extern "C"декларацію.


7
Здається законним. +1 за обґрунтуванням
gx_

Нарешті, цілком чітке пояснення цього. Дякую тонна!
Даніель Сутар

16

Я згоден з відповіддю професора Фалькена , але після коментаря Арне Мерца я хочу навести повний приклад (найважливіша частина - це #ifdef __cplusplus):

somecode.h

#ifndef H_SOMECODE
#define H_SOMECODE

#ifdef __cplusplus
extern "C" {
#endif

void foo(void);

#ifdef __cplusplus
}
#endif

#endif /* H_SOMECODE */

somecode.c

#include "somecode.h"

void foo(void)
{
    /* ... */
}

othercode.hpp

#ifndef HPP_OTHERCODE
#define HPP_OTHERCODE

void bar();

#endif /* HPP_OTHERCODE */

othercode.cpp

#include "othercode.hpp"
#include "somecode.h"

void bar()
{
    foo(); // call C function
    // ...
}

Потім ви виконуєте вказівки професора Фалькена щодо складання та посилання.

Це працює, тому що під час компіляції gccмакрос __cplusplusне визначений, тому заголовок, що somecode.hвходить до нього, somecode.cє таким після попередньої обробки:

void foo(void);

і при компіляції з g++, то __cplusplus це визначається, і тому заголовок включений в othercode.cppданий час так:

extern "C" {

void foo(void);

}

4
thb, мені не подобається #ifdef __cplusplusкод С. Код С є нижчим рівнем, і йому не слід турбуватися, якщо його колись можна буде викликати з коду С ++. Imo, #ifdefякий використовується лише в коді C ++, якщо ви хочете надати заголовок прив'язки C для бібліотеки, написаної на C ++, а не навпаки.
Арне Мерц,

2
@ Prof.Falken, звичайно, але це визначення, призначене для того, щоб забезпечити сумісність коду С ++, а не для коду С ++.
Арне Мерц,

1

Ця відповідь натхненна випадком, коли аргументація Арне була правильною. Постачальник написав бібліотеку, яка колись підтримувала як C, так і C ++; однак остання версія підтримувала лише C. Наступні рудиментарні вказівки, залишені в коді, вводили в оману:

#ifdef __cplusplus
extern "C" {
#endif

Це коштувало мені кількох годин спроби скомпілювати в C ++. Просто зателефонувати на C з C ++ було набагато простіше.

Конвенція ifdef __cplusplus порушує принцип єдиної відповідальності. Код, що використовує цю конвенцію, намагається зробити дві речі одночасно:

  • (1) виконати функцію на C - і -
  • (2) виконати ту саму функцію в C ++

Це все одно, що намагатися писати одночасно американською та британською англійською мовами. Це зайве вкидання гайкового ключа #ifdef __thequeensenglish #elif __yankeeenанглійський ключ # інакше марний інструмент, який ускладнює читання коду #endif в код.

Для простого коду та невеликих бібліотек може працювати ifdef __cplusplus; однак для складних бібліотек найкраще вибрати ту чи іншу мову і дотримуватися її. Підтримка однієї з мов потребує менше обслуговування, ніж спроба підтримувати обидві.

Це запис змін, які я вніс у код Arne, щоб змусити його скомпілювати на Ubuntu Linux.

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"
#include <stdio.h>

void foo(void)
{
     // modified to verify the code was called
     printf("This Hello World was called in C++ and written in C\n");
}

bar.cpp

extern "C" {
    #include "foo.h" //a C header, so wrap it in extern "C" 
}

int main() {
  foo();
  return(0);
}

Makefile

# -*- MakeFile -*-
# dont forget to use tabs, not spaces for indents
# to use simple copy this file in the same directory and type 'make'

myfoobar: bar.o foo.o
    g++ -o myfoobar foo.o bar.o 

bar.o: bar.cpp
    g++ -c -o bar.o bar.cpp

foo.o: foo.c
    gcc -c -o foo.o foo.c
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.