Декомпілюйте g++
створений двійковий файл, щоб побачити, що відбувається
Щоб зрозуміти, чому extern
це необхідно, найкраще зробити, це зрозуміти, що докладно відбувається в об’єктних файлах на прикладі:
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Компілюйте з GCC 4.8 Linux ELF output:
g++ -c main.cpp
Декомпілюйте таблицю символів:
readelf -s main.o
Вихід містить:
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
Інтерпретація
Ми бачимо, що:
ef
і eg
зберігалися в символах з тим же найменуванням, що і в коді
інші символи були зіпсовані. Розв’яжемо їх:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Висновок: обидва наступні типи символів не були змінені:
- визначений
- оголошено, але невизначено (
Ndx = UND
), надаватимуться за посиланням або час запуску з іншого файлу об'єкта
Тож вам потрібно буде extern "C"
і те, і інше:
- C від C ++: скажіть,
g++
щоб очікувати беззмінних символів, вироблених користувачемgcc
- C ++ від C: скажіть
g++
генерувати беззмінні символи для gcc
використання
Речі, які не працюють у зовнішній С
Стає очевидним, що будь-яка функція C ++, яка вимагає керування іменами, не буде працювати всередині 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) { }
}
Мінімальний цикл, що можна виконати на прикладі C ++
Для повноти та для новичок там див. Також: Як використовувати вихідні файли C у проекті C ++?
Викликати C із C ++ досить просто: кожна функція C має лише один можливий некерований символ, тому додаткової роботи не потрібно.
main.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
гл
#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
куб
#include "c.h"
int f(void) { return 1; }
Виконати:
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
Без extern "C"
посилання не вдається:
main.cpp:6: undefined reference to `f()'
тому що g++
розраховує знайти згорбленого f
, що gcc
не призвело.
Приклад на GitHub .
Мінімальний цикл C ++ з прикладу C
Викликати C ++ від трохи складніше: нам потрібно створити вручну версії кожної функції, яку ми хочемо викрити.
Тут ми проілюструємо, як піддавати функцію C ++ перевантаження на 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);
}
Виконати:
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
Без extern "C"
цього не вдається:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
тому що g++
генеруються розіграні символи, яких gcc
неможливо знайти.
Приклад на GitHub .
Тестовано в Ubuntu 18.04.