Decompila un g++
binario generato per vedere cosa sta succedendo
Per capire perché extern
è necessario, la cosa migliore da fare è capire cosa sta succedendo in dettaglio nei file oggetto con un esempio:
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Compilare con l' output ELF Linux di GCC 4.8 :
g++ -c main.cpp
Decompilare la tabella dei simboli:
readelf -s main.o
L'output contiene:
Num: Value Size Type Bind Vis Ndx Name
8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 _Z1fv
9: 0000000000000006 6 FUNC GLOBAL DEFAULT 1 ef
10: 000000000000000c 16 FUNC GLOBAL DEFAULT 1 _Z1hv
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
Interpretazione
Lo vediamo:
ef
e eg
sono stati memorizzati in simboli con lo stesso nome del codice
gli altri simboli erano mutilati. Districiamoli:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Conclusione: entrambi i seguenti tipi di simboli non sono stati alterati:
- definito
- dichiarato ma non definito (
Ndx = UND
), da fornire al collegamento o in fase di esecuzione da un altro file oggetto
Quindi avrai bisogno di extern "C"
entrambi quando chiami:
- C da C ++: dire
g++
di aspettarsi simboli non confusi prodotti dagcc
- C ++ da C:
g++
indica di generare simboli non confusi gcc
da usare
Cose che non funzionano nell'esterno C
Diventa ovvio che qualsiasi funzione C ++ che richiede la modifica del nome non funzionerà all'interno extern C
:
extern "C" {
// Overloading.
// error: declaration of C function ‘void f(int)’ conflicts with
void f();
void f(int i);
// Templates.
// error: template with C linkage
template <class C> void f(C i) { }
}
Eseguibile minimo C dall'esempio C ++
Per completezza e per i neofiti là fuori, vedi anche: Come usare i file sorgente C in un progetto C ++?
Chiamare C da C ++ è abbastanza semplice: ogni funzione C ha un solo simbolo non distorto, quindi non è necessario alcun lavoro extra.
main.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
ch
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
cc
#include "c.h"
int f(void) { return 1; }
Correre:
g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out
Senza extern "C"
il collegamento fallisce con:
main.cpp:6: undefined reference to `f()'
perché si g++
aspetta di trovare un uomo mutilato f
, che gcc
non ha prodotto.
Esempio su GitHub .
Esempio C ++ eseguibile minimo da C.
Chiamare C ++ da è un po 'più difficile: dobbiamo creare manualmente versioni non alterate di ogni funzione che vogliamo esporre.
Qui illustriamo come esporre i sovraccarichi della funzione C ++ a C.
main.c
#include <assert.h>
#include "cpp.h"
int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}
cpp.h
#ifndef CPP_H
#define CPP_H
#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif
#endif
cpp.cpp
#include "cpp.h"
int f(int i) {
return i + 1;
}
int f(float i) {
return i + 2;
}
int f_int(int i) {
return f(i);
}
int f_float(float i) {
return f(i);
}
Correre:
gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out
Senza di extern "C"
essa fallisce con:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
perché ha g++
generato simboli alterati che gcc
non è possibile trovare.
Esempio su GitHub .
Testato su Ubuntu 18.04.