Chiama una funzione C dal codice C ++


90

Ho una funzione C che vorrei chiamare da C ++. Non ho potuto " extern "C" void foo()" usare questo tipo di approccio perché la funzione C non è stata compilata usando g ++. Ma si compila bene usando gcc. Qualche idea su come chiamare la funzione da C ++?


1
Potresti scrivere un codice di esempio e g++messaggi di errore
Matthieu Rouget

7
Se lo compili con un compilatore C ++, è C ++. Il codice C non deve essere compilato con un compilatore C ++. Sono lingue diverse. Il tuo codice non è valido C ++ e quindi non viene compilato con un compilatore C ++.
xaxxon

3
@ MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
autistico

2
Il mio tentativo: void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_

1
Questo dovrebbe essere lasciato aperto, soprattutto perché ha buone risposte che sottolineano come il compilatore C (piuttosto che C ++) può essere utilizzato per il codice C.
Chris Stratton

Risposte:


126

Compila il codice C in questo modo:

gcc -c -o somecode.o somecode.c

Quindi il codice C ++ come questo:

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

Quindi collegali insieme, con il linker C ++:

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

Devi anche dire al compilatore C ++ che sta arrivando un'intestazione C quando includi la dichiarazione per la funzione C. Quindi othercode.cppinizia con:

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

somecode.h dovrebbe contenere qualcosa come:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(Ho usato gcc in questo esempio, ma il principio è lo stesso per qualsiasi compilatore. Compila separatamente come C e C ++, rispettivamente, quindi collegalo insieme.)


7
@Arne Good points. Alcune persone interpretano un po 'di C ++ nel loro C avvolgendo extern "C"nell'intestazione con #ifdef __cplusplus.
rilassarsi

@Arne Vedi la mia risposta qui sotto. rilassati Come puoi vedere sono uno di loro;)
gx_

1
Grazie mille ! Mi è stato molto utile :)
Hesham Eraqi

Stavo ricevendo il seguente errore: Errore: # 337: la specifica di collegamento è incompatibile con il precedente "foo" (dichiarato alla riga 1) Ora la sua compilazione va bene. Qualcuno può spiegare?
FaizanHussainRabbani

@FaizanRabbani, non senza molti più dettagli.
Prof.Falken

61

Consentitemi di raccogliere i pezzi dalle altre risposte e commenti, per darvi un esempio con codice C e C ++ ben separato:

La parte C:

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

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

Compilalo con gcc -c -o foo.o foo.c.

La parte C ++:

bar.cpp

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

void bar() {
  foo();
}

Compilalo con g++ -c -o bar.o bar.cpp

E poi collega tutto insieme:

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

Razionale: il codice C dovrebbe essere un semplice codice C, non significa #ifdef"forse un giorno lo chiamerò da un'altra lingua". Se qualche programmatore C ++ chiama le tue funzioni C, è il loro problema come farlo, non il tuo. E se sei il programmatore C ++, allora l'intestazione C potrebbe non essere tua e non dovresti cambiarla, quindi la gestione dei nomi di funzione non confusi (cioè il extern "C") appartiene al tuo codice C ++.

Potresti, ovviamente, scrivere una comoda intestazione C ++ che non fa altro che avvolgere l'intestazione C in una extern "C"dichiarazione.


7
Sembra giusto. +1 per la logica
gx_

Finalmente una spiegazione completamente chiara di questo. Grazie mille!
Daniel Soutar

16

Sono d'accordo con la risposta del Prof.Falken , ma dopo il commento di Arne Mertz voglio fare un esempio completo (la parte più importante è la #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
    // ...
}

Quindi segui le istruzioni del Prof. Falken per compilare e collegare.

Funziona perché durante la compilazione con gcc, la macro__cplusplus non è definita, quindi l'intestazione somecode.hinclusa in somecode.cè così dopo la preelaborazione:

void foo(void);

e quando si compila con g++, then __cplusplus è definito, e quindi l'intestazione inclusa in othercode.cppè ora così:

extern "C" {

void foo(void);

}

4
thb, non mi piace il #ifdef __cpluspluscodice in C. Il codice C è il livello inferiore e non dovrebbe preoccuparsi se un giorno potrebbe essere chiamato dal codice C ++. Imo che #ifdefha il suo uso solo nel codice C ++ se si desidera fornire un'intestazione di associazione C per una libreria scritta in C ++, non il contrario.
Arne Mertz

2
@ Prof.Falken ovviamente, ma è una definizione pensata per essere in grado di fornire la compatibilità "verso il basso" del codice C ++, non per il codice C.
Arne Mertz

1

Questa risposta è ispirata da un caso in cui la logica di Arne era corretta. Un venditore ha scritto una libreria che una volta supportava sia C che C ++; tuttavia, l'ultima versione supportava solo C.Le seguenti direttive vestigiali lasciate nel codice erano fuorvianti:

#ifdef __cplusplus
extern "C" {
#endif

Questo mi è costato diverse ore cercando di compilare in C ++. Chiamare semplicemente C da C ++ è stato molto più semplice.

La convenzione ifdef __cplusplus viola il principio di responsabilità unica. Un codice che utilizza questa convenzione sta cercando di fare due cose contemporaneamente:

  • (1) eseguire una funzione in C - e -
  • (2) eseguire la stessa funzione in C ++

È come cercare di scrivere contemporaneamente in inglese americano e britannico. Questo sta lanciando inutilmente una chiave #ifdef __thequeensenglish #elif __yankeeenglish wrench #else uno strumento inutile che rende il codice più difficile da leggere #endif nel codice.

Per codice semplice e piccole librerie la convenzione ifdef __cplusplus può funzionare; tuttavia, per biblioteche complesse è meglio scegliere una lingua o l'altra e attenersi ad essa. Supportare una delle lingue richiederà meno manutenzione rispetto al tentativo di supportare entrambe.

Questa è una registrazione delle modifiche che ho apportato al codice di Arne per farlo compilare su 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.