Łączenie C++ i C-jak działa # ifdef cplusplus?

Pracuję nad projektem, który ma spore dziedzictwo C kodu. Zaczęliśmy pisać w C++, z zamiarem ostatecznie przekonwertowania kodu starszego. Jestem trochę zdezorientowany, jak C i C++ współdziałają. Rozumiem, że zawijając kod C z extern "C" kompilator C++ nie zmieni nazw kodu C , ale nie jestem do końca pewien, jak to zaimplementować.

Tak więc na górze każdego C pliku nagłówkowego (po include guards), mamy

#ifdef __cplusplus
extern "C" {
#endif

I na dole piszemy

#ifdef __cplusplus
}
#endif

Pomiędzy tymi dwoma, mamy wszystkie nasze includes, typedefs i prototypy funkcji. Mam kilka pytań, aby sprawdzić, czy dobrze to Rozumiem: {]}

  1. Jeśli mam plik C++ A. hh który zawiera plik nagłówkowy C B. h, zawiera inny C plik nagłówkowy C. h, jak to działa? Myślę, że gdy kompilator wchodzi w B. h, __cplusplus zostanie zdefiniowana, więc Wola zawiń kod za pomocą extern "C" (i __cplusplus nie będzie zdefiniowanych wewnątrz tego bloku). Więc, kiedy wchodzi do C. h, __cplusplus nie zostanie zdefiniowana a kod nie będzie owinięty w extern "C". Czy to prawda?

  2. Czy coś jest nie tak z owijanie kawałka kodu z extern "C" { extern "C" { .. } }? Co będzie drugie extern "C" zrobić?

  3. Nie kładziemy tego opakowania wokół .pliki c, tylko ... pliki H. Co się stanie, jeśli funkcja nie ma prototypu? Czy kompilator uważa, że jest to Funkcja C++?

  4. Korzystamy również z usług osób trzecich kod, który jest zapisany w C i robi nie mam takiego opakowania to. Za każdym razem dołączam nagłówek z tej biblioteki, wkładałem an extern "C" wokół # include. Czy to właściwy sposób radzenia sobie z to?

  5. Wreszcie, czy to jest dobry pomysł? Czy jest coś jeszcze, co powinniśmy zrobić? Będziemy mieszać C i C++ w przewidywalnej przyszłości, a ja chcesz się upewnić zajmujemy się wszystkim nasze bazy.

Author: Jonathan Leffler, 2010-09-24

3 answers

extern "C" to nie zmienia sposobu, w jaki kompilator odczytuje kod. Jeśli twój kod jest w .plik c, zostanie skompilowany jako C, jeśli znajduje się wplik cpp, zostanie skompilowany jako C++ (chyba, że zrobisz coś dziwnego w konfiguracji).

To, co extern "C" ma wpływ na połączenie. Funkcje C++, po skompilowaniu, mają swoje nazwy zniekształcone - to sprawia, że przeciążenie jest możliwe. Nazwa funkcji jest modyfikowana na podstawie typów i liczby parametrów, tak aby dwie funkcje z ta sama nazwa będzie miała różne nazwy symboli.

Kod wewnątrz {[0] } jest nadal kodem C++. Istnieją ograniczenia co do tego, co można zrobić w bloku extern "C", ale wszystkie dotyczą linkowania. Nie można zdefiniować żadnych nowych symboli, które nie mogą być budowane za pomocą C linkage. Oznacza to na przykład brak klas lub szablonów.

extern "C" klocki ładnie gniazdują. Jest też extern "C++" jeśli znajdziesz się beznadziejnie uwięziony wewnątrz extern "C" regionów, ale to nie jest taki dobry pomysł z czystości perspektywa.

Teraz, szczególnie w odniesieniu do Twoich ponumerowanych pytań:]}

Regarding #1: _ _ cplusplus powinien być zdefiniowany wewnątrz extern "C" bloków. Nie ma to jednak znaczenia, ponieważ bloki powinny dobrze zagnieżdżać się.

Odnośnie #2: _ _ cplusplus będzie zdefiniowany dla każdej jednostki kompilacji, która jest uruchamiana przez kompilator C++. Ogólnie to znaczy .pliki cpp i wszelkie pliki dołączane przez to .plik cpp. To samo .h (lub .hh lub .hpp czy co-masz - ty) może być interpretowane jako C lub c++ w różnym czasie, jeśli zawierają je różne jednostki kompilacji. Jeśli chcesz prototypy w .plik h, aby odnosić się do nazw symboli C, musi mieć extern "C", gdy jest interpretowany jako C++, a nie powinien mieć extern "C", gdy jest interpretowany jako C -- stąd sprawdzanie #ifdef __cplusplus.

Aby odpowiedzieć na twoje pytanie #3: Funkcje Bez prototypów będą miały łącze C++, jeśli są w .pliki cpp, a nie Wewnątrz extern "C" bloku. Jest to jednak w porządku, ponieważ jeśli ma nie ma prototypu, może być wywoływana tylko przez inne funkcje w tym samym pliku, a potem ogólnie nie obchodzi cię, jak wygląda powiązanie, ponieważ nie planujesz, aby ta funkcja była wywoływana przez cokolwiek poza tą samą jednostką kompilacyjną.

Dla #4, masz to dokładnie. Jeśli dodajesz nagłówek do kodu, który ma podlinkowanie C (takiego jak kod skompilowany przez kompilator C), musisz extern "C" nagłówek -- w ten sposób będziesz mógł połączyć się z biblioteką. (W przeciwnym razie Twój linker szukałby funkcji o nazwach takich jak _Z1hic, gdy ty szukałeś void h(int, char)

5: ten rodzaj mieszania jest częstym powodem, aby używać extern "C", i nie widzę nic złego w robieniu tego w ten sposób-po prostu upewnij się, że rozumiesz, co robisz.

 223
Author: Andrew Shelansky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-24 17:32:50
  1. extern "C" nie zmienia obecności lub braku makra __cplusplus. To tylko zmienia linkowanie i wymazywanie nazw owiniętych deklaracji.

  2. Możesz zagnieżdżać extern "C" klocki całkiem szczęśliwie.

  3. Jeśli skompilujesz swoje pliki .c jako C++, to wszystko, co nie znajduje się w bloku extern "C" i bez prototypu extern "C", będzie traktowane jako funkcja C++. Jeśli skompilujesz je jako C to oczywiście wszystko będzie C funkcja.

  4. Tak

  5. W ten sposób można bezpiecznie mieszać C i C++.

 33
Author: Anthony Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-27 11:02:52

Kilka gotchas, które są kolorami do doskonałej odpowiedzi Andrew Shelansky ' ego i trochę się nie zgodzić z nie zmienia sposobu, w jaki kompilator odczytuje kod

Ponieważ prototypy funkcji są kompilowane jako C, nie można przeciążać tych samych nazw funkcji o różnych parametrach - to jedna z kluczowych cech zniekształcania nazwy kompilatora. Jest to opisane jako problem z połączeniem, ale to nie do końca prawda - otrzymasz błędy z obu kompilator i linker.

Błędy kompilatora pojawią się, jeśli spróbujesz użyć funkcji C++ deklaracji prototypu, takich jak przeciążenie.

Błędy linkera pojawią się później, ponieważ twoja funkcja nie zostanie znaleziona, jeślinie MASZextern "C" Owijacz wokół deklaracji i nagłówek jest zawarty w mieszaninie źródeł C i C++.

Jednym z powodów, aby zniechęcić ludzi do używania kompilacji C jako C++ jest to, że oznacza to, że ich kod źródłowy nie jest już przenośny. To ustawienie jest ustawieniem projektu, a więc jeśli .plik c jest wrzucany do innego projektu, nie zostanie skompilowany jako c++. Wolałbym, żeby ludzie poświęcili czas na zmianę nazwy pliku na .cpp.

 16
Author: Andy Dent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-26 08:15:16