Rufen Sie eine C-Funktion aus C ++ - Code auf

87

Ich habe eine C-Funktion, die ich aus C ++ aufrufen möchte. Ich konnte keinen extern "C" void foo()Ansatz verwenden, da die C-Funktion nicht mit g ++ kompiliert werden konnte. Aber es kompiliert gut mit gcc. Irgendwelche Ideen, wie man die Funktion aus C ++ aufruft?

Dangila
quelle
1
Könnten Sie bitte einige Beispielcode und g++Fehlermeldungen schreiben
Matthieu Rouget
7
Wenn Sie es mit einem C ++ - Compiler kompilieren, ist es C ++. C-Code muss nicht mit einem C ++ - Compiler kompiliert werden. Sie sind verschiedene Sprachen. Ihr Code ist in C ++ nicht gültig und wird daher nicht mit einem C ++ - Compiler kompiliert.
Xaxxon
3
@ MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
autistisch
2
Mein Versuch: void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_
1
Dies sollte offen bleiben, insbesondere da es gute Antworten gibt, die darauf hinweisen, wie der C-Compiler (anstelle von C ++) für den C-Code verwendet werden kann.
Chris Stratton

Antworten:

122

Kompilieren Sie den C-Code wie folgt:

gcc -c -o somecode.o somecode.c

Dann lautet der C ++ - Code wie folgt:

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

Verknüpfen Sie sie dann mit dem C ++ - Linker:

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

Sie müssen dem C ++ - Compiler auch mitteilen, dass ein C-Header kommt, wenn Sie die Deklaration für die C-Funktion einfügen. Also othercode.cppbeginnt mit:

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

somecode.h sollte etwas enthalten wie:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(In diesem Beispiel habe ich gcc verwendet, aber das Prinzip ist für jeden Compiler dasselbe. Erstellen Sie es separat als C bzw. C ++ und verknüpfen Sie es dann miteinander.)

Prof. Falken
quelle
7
@Arne Gute Punkte. Einige Leute schmieren etwas C ++ in ihrem C, indem sie das extern "C"in den Header einschließen #ifdef __cplusplus.
Entspannen Sie
@Arne Siehe meine Antwort unten. Wie Sie sehen, bin ich einer von ihnen;)
gx_
1
Ich danke dir sehr ! Das war sehr nützlich für mich :)
Hesham Eraqi
Ich habe den folgenden Fehler erhalten: Fehler: # 337: Die Verknüpfungsspezifikation ist nicht kompatibel mit dem vorherigen "foo" (in Zeile 1 deklariert). Jetzt ist die Kompilierung in Ordnung. Kann jemand erklären?
FaizanHussainRabbani
@FaizanRabbani, nicht ohne viel mehr Details.
Prof. Falken
61

Lassen Sie mich die Teile der anderen Antworten und Kommentare zusammenfassen, um Ihnen ein Beispiel mit sauber getrenntem C- und C ++ - Code zu geben:

Der C-Teil:

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

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

Kompilieren Sie dies mit gcc -c -o foo.o foo.c.

Der C ++ - Teil:

bar.cpp

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

void bar() {
  foo();
}

Kompilieren Sie dies mit g++ -c -o bar.o bar.cpp

Und dann alles miteinander verbinden:

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

Begründung: Der C-Code sollte ein einfacher C-Code sein, Nr#ifdef s für "Vielleicht rufe ich das eines Tages aus einer anderen Sprache auf". Wenn einige C ++ Programmierer Ihre C - Funktionen aufruft, es ist ihr Problem , wie das zu tun, nicht verkaufen. Und wenn Sie der C ++ - Programmierer sind, gehört der C-Header möglicherweise nicht Ihnen und Sie sollten ihn nicht ändern, sodass die Behandlung nicht entwirrter Funktionsnamen (dh der extern "C") in Ihren C ++ - Code gehört.

Sie können sich natürlich auch einen praktischen C ++ - Header schreiben, der nichts anderes tut, als den C-Header in eine extern "C"Deklaration zu verpacken .

Arne Mertz
quelle
6
Klingt plausibel. +1 für die Begründung
gx_
Zum Schluss noch eine ganz klare Erklärung dafür. Danke vielmals!
Daniel Soutar
16

Ich stimme der Antwort von Prof. Falken zu , aber nach dem Kommentar von Arne Mertz möchte ich ein vollständiges Beispiel geben (der wichtigste Teil ist der #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
    // ...
}

Dann folgen Sie den Anweisungen von Prof. Falken zum Kompilieren und Verknüpfen.

Dies funktioniert , weil , wenn Sie mit dem Kompilieren gcc, das Makro __cplusplusnicht definiert ist , so dass der Header somecode.henthalten in somecode.cwie dies nach der Vorverarbeitung:

void foo(void);

und wenn Sie mit dem Kompilieren g++, dann __cplusplus wird definiert, und so den Header enthalten in othercode.cppist nun wie folgt aus:

extern "C" {

void foo(void);

}
gx_
quelle
4
thb, ich mag den #ifdef __cplusplusin C-Code nicht. Der C-Code ist die untere Ebene und sollte sich nicht darum kümmern müssen, ob er eines Tages aus dem C ++ - Code aufgerufen wird. Imo, #ifdefdas nur in C ++ - Code verwendet wird, wenn Sie einen C-Bindungsheader für eine in C ++ geschriebene Bibliothek bereitstellen möchten, nicht umgekehrt.
Arne Mertz
2
@ Prof.Falken natürlich, aber es ist eine Definition, die in der Lage sein soll, "Abwärts" -Kompatibilität von C ++ - Code bereitzustellen, nicht für C-Code.
Arne Mertz
1

Diese Antwort ist inspiriert von einem Fall, in dem Arnes Begründung richtig war. Ein Anbieter hat eine Bibliothek geschrieben, die einst sowohl C als auch C ++ unterstützte. Die neueste Version unterstützte jedoch nur C. Die folgenden im Code verbliebenen Anweisungen waren irreführend:

#ifdef __cplusplus
extern "C" {
#endif

Der Versuch, in C ++ zu kompilieren, hat mich mehrere Stunden gekostet. Das einfache Aufrufen von C aus C ++ war viel einfacher.

Die ifdef __cplusplus-Konvention verstößt gegen das Prinzip der Einzelverantwortung. Ein Code, der diese Konvention verwendet, versucht zwei Dinge gleichzeitig zu tun:

  • (1) eine Funktion in C ausführen - und -
  • (2) Führen Sie dieselbe Funktion in C ++ aus

Es ist, als würde man versuchen, gleichzeitig in amerikanischem und britischem Englisch zu schreiben. Dies wirft unnötigerweise einen #ifdef __thequeensenglish Schraubenschlüssel #elif __yankeeenglish Schraubenschlüssel #else ein nutzloses Werkzeug, das es schwieriger macht, den Code #endif in den Code einzulesen.

Für einfachen Code und kleine Bibliotheken funktioniert möglicherweise die Konvention ifdef __cplusplus. Für komplexe Bibliotheken ist es jedoch am besten, die eine oder andere Sprache auszuwählen und dabei zu bleiben. Die Unterstützung einer der Sprachen erfordert weniger Wartung als der Versuch, beide zu unterstützen.

Dies ist eine Aufzeichnung der Änderungen, die ich an Arnes Code vorgenommen habe, damit er unter Ubuntu Linux kompiliert werden kann.

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
Landwirt
quelle