Wywołaj funkcję C z kodu C ++

90

Mam funkcję C, którą chciałbym wywołać z C ++. Nie mogłem użyć extern "C" void foo()podejścia typu „ ”, ponieważ funkcja C nie została skompilowana przy użyciu g ++. Ale kompiluje się dobrze przy użyciu gcc. Jakieś pomysły, jak wywołać funkcję z C ++?

Dangila
źródło
1
Czy mógłbyś napisać przykładowy kod i g++komunikaty o błędach
Matthieu Rouget
7
Jeśli kompilujesz go za pomocą kompilatora C ++, jest to C ++. Kod C nie musi kompilować za pomocą kompilatora C ++. To są różne języki. Twój kod nie jest prawidłowy w C ++ i dlatego nie kompiluje się za pomocą kompilatora C ++.
xaxxon
3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
autystyczny
2
Moja próba: void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_
1
Powinno to pozostać otwarte, zwłaszcza, że ​​zawiera dobre odpowiedzi wskazujące, jak kompilator C (zamiast C ++) może być użyty do kodu C.
Chris Stratton

Odpowiedzi:

126

Skompiluj kod C w następujący sposób:

gcc -c -o somecode.o somecode.c

Następnie kod C ++ taki jak ten:

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

Następnie połącz je ze sobą za pomocą konsolidatora C ++:

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

Musisz także powiedzieć kompilatorowi C ++, że nagłówek C nadchodzi, gdy dołączasz deklarację funkcji C. Więc othercode.cppzaczyna się od:

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

somecode.h powinien zawierać coś takiego:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(W tym przykładzie użyłem gcc, ale zasada jest taka sama dla każdego kompilatora. Skompiluj osobno odpowiednio jako C i C ++, a następnie połącz je razem.)

Prof. Falken
źródło
7
@Arne Dobre punkty. Niektórzy ludzie rozumieją niektóre C ++ w swoim C, umieszczając extern "C"w nagłówku znak #ifdef __cplusplus.
odpoczynek
@Arne Zobacz moją odpowiedź poniżej. odpręż się Jak widać, jestem jednym z nich;)
gx_
1
Dziękuję bardzo ! To było dla mnie bardzo przydatne :)
Hesham Eraqi
Otrzymałem następujący błąd: Błąd: # 337: specyfikacja powiązania jest niekompatybilna z poprzednim "foo" (zadeklarowanym w linii 1). Teraz kompilacja jest w porządku. Czy ktoś może wyjaśnić?
FaizanHussainRabbani
@FaizanRabbani, nie bez większej ilości szczegółów.
Prof. Falken,
61

Pozwólcie mi zebrać fragmenty z innych odpowiedzi i komentarzy, aby dać przykład z czysto oddzielonymi kodami C i C ++:

Część C:

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

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

Skompiluj to z gcc -c -o foo.o foo.c.

Część C ++:

bar.cpp

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

void bar() {
  foo();
}

Skompiluj to z g++ -c -o bar.o bar.cpp

A potem połącz to wszystko razem:

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

Uzasadnienie: kod C powinien być zwykłym kodem w języku C, bez #ifdef„może kiedyś zadzwonię z innego języka”. Jeśli jakiś programista C ++ wywołuje twoje funkcje C, to ich problem, jak to zrobić, a nie twój. A jeśli jesteś programistą C ++, to nagłówek C może nie być twój i nie powinieneś go zmieniać, więc obsługa niezarządzanych nazw funkcji (tj. extern "C") Należy do twojego kodu C ++.

Możesz oczywiście napisać sobie wygodny nagłówek C ++, który nie robi nic poza opakowaniem nagłówka C w extern "C"deklarację.

Arne Mertz
źródło
7
Wydaje się uzasadnione. +1 dla uzasadnienia
gx_
Wreszcie całkowicie jasne wyjaśnienie tego. Wielkie dzięki!
Daniel Soutar
16

Zgadzam się z odpowiedzią prof. Falkena , ale po komentarzu Arne Mertza chcę podać pełny przykład (najważniejsza część to #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
    // ...
}

Następnie postępuj zgodnie z instrukcjami prof. Falkena, aby skompilować i połączyć.

Działa to, ponieważ podczas kompilacji z gccmakro __cplusplusnie jest zdefiniowane, więc nagłówek somecode.hzawarty w programie somecode.cwygląda tak po przetworzeniu wstępnym:

void foo(void);

a podczas kompilacji z g++, to __cplusplus jest zdefiniowane, więc nagłówek zawarty w othercode.cppjest teraz taki:

extern "C" {

void foo(void);

}
gx_
źródło
4
thb, nie podoba mi się #ifdef __cpluspluskod w C. Kod C jest niższym poziomem i nie powinno się martwić, jeśli pewnego dnia zostanie wywołany z kodu C ++. Imo, które #ifdefma swoje zastosowanie tylko w kodzie C ++, jeśli chcesz zapewnić nagłówek powiązania C dla biblioteki napisanej w C ++, a nie odwrotnie.
Arne Mertz
2
@ Prof.Falken oczywiście, ale jest to definicja, która ma być w stanie zapewnić kompatybilność „w dół” kodu C ++, a nie kodu C.
Arne Mertz
1

Ta odpowiedź jest inspirowana przypadkiem, w którym uzasadnienie Arne'a było poprawne. Dostawca napisał bibliotekę, która kiedyś obsługiwała zarówno C, jak i C ++; Jednak najnowsza wersja obsługiwała tylko C. Następujące śladowe dyrektywy pozostawione w kodzie wprowadzały w błąd:

#ifdef __cplusplus
extern "C" {
#endif

Kosztowało mnie to kilka godzin przy próbie kompilacji w C ++. Samo wywołanie C z C ++ było znacznie łatwiejsze.

Konwencja ifdef __cplusplus narusza zasadę pojedynczej odpowiedzialności. Kod korzystający z tej konwencji próbuje jednocześnie wykonać dwie rzeczy:

  • (1) wykonaj funkcję w C - i -
  • (2) wykonują tę samą funkcję w C ++

To tak, jakby próbować pisać jednocześnie po angielsku amerykańskim i brytyjskim. Jest to niepotrzebne wrzucenie #ifdef __thequeensenglish spanner #elif __yankeeenglish wrench #else bezużytecznego narzędzia, które utrudnia odczytanie kodu #endif w kodzie.

W przypadku prostego kodu i małych bibliotek może działać konwencja ifdef __cplusplus; jednakże w przypadku złożonych bibliotek najlepiej jest wybrać jeden lub drugi język i trzymać się go. Obsługa jednego z języków wymaga mniej konserwacji niż próba obsługi obu.

To jest zapis modyfikacji, które wprowadziłem w kodzie Arne, aby skompilować go w systemie 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
Agronom
źródło