Saya sedang mengerjakan proyek yang memiliki banyak kode C sebelumnya . Kami sudah mulai menulis dalam C ++, dengan tujuan untuk akhirnya mengonversi kode lama juga. Saya agak bingung tentang bagaimana C dan C ++ berinteraksi. Saya mengerti bahwa dengan membungkus kode C dengan extern "C"
kompiler C ++ tidak akan memotong-motong nama kode C , tapi saya tidak sepenuhnya yakin bagaimana menerapkannya.
Jadi, di bagian atas setiap file header C (setelah penjaga termasuk), kita miliki
#ifdef __cplusplus
extern "C" {
#endif
dan di bagian bawah, kita menulis
#ifdef __cplusplus
}
#endif
Di antara keduanya, kami memiliki semua prototipe fungsi, typedef, dan fungsi kami. Saya punya beberapa pertanyaan, untuk melihat apakah saya memahami ini dengan benar:
Jika saya memiliki file C ++ A.hh yang mencakup file header C Bh, termasuk file header C lain Ch, bagaimana cara kerjanya? Saya pikir ketika kompiler melangkah ke Bh,
__cplusplus
akan ditentukan, sehingga akan membungkus kode denganextern "C"
(dan__cplusplus
tidak akan didefinisikan di dalam blok ini). Jadi, ketika melangkah ke Ch,__cplusplus
tidak akan ditentukan dan kode tidak akan dibungkusextern "C"
. Apakah ini benar?Apakah ada yang salah dengan membungkus sepotong kode
extern "C" { extern "C" { .. } }
? Apa yang akanextern "C"
dilakukan yang kedua ?Kami tidak meletakkan pembungkus ini di sekitar file .c, hanya file .h. Jadi, apa yang terjadi jika suatu fungsi tidak memiliki prototipe? Apakah kompiler berpikir bahwa itu adalah fungsi C ++?
Kami juga menggunakan beberapa kode pihak ketiga yang ditulis dalam C , dan tidak memiliki pembungkus semacam ini di sekitarnya. Setiap kali saya memasukkan tajuk dari perpustakaan itu, saya telah meletakkan di
extern "C"
sekitar #include. Apakah ini cara yang tepat untuk menghadapinya?Akhirnya, apakah ini merupakan ide bagus? Apakah ada hal lain yang harus kita lakukan? Kita akan mencampur C dan C ++ untuk masa yang akan datang, dan saya ingin memastikan bahwa kami mencakup semua basis kami.
sumber
To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed.
(Saya mendapatkannya dari tautan )Jawaban:
extern "C"
tidak benar-benar mengubah cara kompiler membaca kode. Jika kode Anda dalam file .c, itu akan dikompilasi sebagai C, jika itu dalam file .cpp, itu akan dikompilasi sebagai C ++ (kecuali jika Anda melakukan sesuatu yang aneh dengan konfigurasi Anda).Apa yang
extern "C"
dilakukan adalah memengaruhi pertautan. Fungsi-fungsi C ++, ketika dikompilasi, memiliki nama-nama mereka yang hancur - inilah yang memungkinkan overloading terjadi. Nama fungsi akan dimodifikasi berdasarkan pada jenis dan jumlah parameter, sehingga dua fungsi dengan nama yang sama akan memiliki nama simbol yang berbeda.Kode di dalam sebuah
extern "C"
masih merupakan kode C ++. Ada batasan pada apa yang dapat Anda lakukan di blok "C" eksternal, tetapi semuanya tentang keterkaitan. Anda tidak dapat menentukan simbol baru yang tidak dapat dibangun dengan tautan C. Itu berarti tidak ada kelas atau template, misalnya.extern "C"
blok sarang dengan baik. Ada jugaextern "C++"
jika Anda menemukan diri Anda benar-benar terjebak di dalamextern "C"
wilayah, tetapi itu bukan ide yang bagus dari sudut pandang kebersihan.Sekarang, khusus tentang pertanyaan bernomor Anda:
Mengenai # 1: __cplusplus akan tetap didefinisikan di dalam
extern "C"
blok. Ini tidak masalah, karena blok harus bersarang dengan rapi.Mengenai # 2: __cplusplus akan didefinisikan untuk setiap unit kompilasi yang dijalankan melalui kompiler C ++. Secara umum, itu berarti file .cpp dan semua file yang disertakan oleh file .cpp itu. .H (atau .hh atau .hpp atau what-you-you) yang sama dapat diartikan sebagai C atau C ++ pada waktu yang berbeda, jika unit kompilasi yang berbeda memasukkannya. Jika Anda ingin prototipe dalam file .h merujuk ke nama simbol C, maka mereka harus memiliki
extern "C"
ketika ditafsirkan sebagai C ++, dan mereka tidak harusextern "C"
ketika ditafsirkan sebagai C - maka#ifdef __cplusplus
pemeriksaan.Untuk menjawab pertanyaan Anda # 3: fungsi tanpa prototipe akan memiliki pertautan C ++ jika mereka ada dalam file .cpp dan bukan di dalam suatu
extern "C"
blok. Ini baik-baik saja, karena, jika tidak memiliki prototipe, itu hanya dapat dipanggil oleh fungsi lain dalam file yang sama, dan kemudian Anda biasanya tidak peduli seperti apa hubungan itu, karena Anda tidak berencana memiliki fungsi itu dipanggil oleh apa pun di luar unit kompilasi yang sama pula.Untuk # 4, Anda mendapatkannya dengan tepat. Jika Anda menyertakan tajuk untuk kode yang memiliki tautan C (seperti kode yang dikompilasi oleh kompiler C), maka Anda harus
extern "C"
tajuk - dengan cara itu Anda akan dapat menautkan dengan pustaka. (Kalau tidak, penghubung Anda akan mencari fungsi dengan nama seperti_Z1hic
saat Anda mencarivoid h(int, char)
5: Pencampuran semacam ini adalah alasan umum untuk digunakan
extern "C"
, dan saya tidak melihat ada yang salah dengan melakukannya dengan cara ini - pastikan Anda mengerti apa yang Anda lakukan.sumber
extern "C++"
kapan header / kode C ++ Anda terperangkap jauh di dalam beberapa kode C__cplusplus
adalah untuk menentukan apakahC++
sedang digunakan vsC
, jadi mendefinisikannya secara manual / secara eksplisit menentang tujuan itu ...extern "C"
tidak mengubah ada atau tidaknya__cplusplus
makro. Itu hanya mengubah tautan dan susunan nama dari deklarasi yang dibungkus.Anda dapat membuat sarang
extern "C"
dengan cukup bahagia.Jika Anda mengkompilasi
.c
file Anda sebagai C ++ maka apa pun yang tidak diextern "C"
blok, dan tanpaextern "C"
prototipe akan diperlakukan sebagai fungsi C ++. Jika Anda mengkompilasinya sebagai C maka tentu saja semuanya akan menjadi fungsi C.Iya
Anda dapat dengan aman mencampurkan C dan C ++ dengan cara ini.
sumber
.c
file sebagai C ++, maka semuanya dikompilasi sebagai kode C ++, bahkan jika itu dalam sebuahextern "C"
blok. Theextern "C"
kode tidak dapat menggunakan fitur yang bergantung pada C ++ memanggil konvensi (misalnya operator overloading) tapi tubuh fungsi masih disusun sebagai C ++, dengan semua yang memerlukan.Sepasang gotcha yang merupakan colloraries untuk jawaban terbaik Andrew Shelansky dan untuk sedikit tidak setuju tidak benar-benar mengubah cara kompiler membaca kode
Karena prototipe fungsi Anda dikompilasi sebagai C, Anda tidak dapat memiliki overloading dari nama fungsi yang sama dengan parameter yang berbeda - itulah salah satu fitur kunci dari pembuatan nama kompiler. Ini dideskripsikan sebagai masalah tautan tetapi itu tidak sepenuhnya benar - Anda akan mendapatkan kesalahan dari penyusun dan tautan.
Kesalahan kompiler akan terjadi jika Anda mencoba menggunakan fitur-fitur C ++ dari deklarasi prototipe seperti overloading.
Kesalahan tautan akan terjadi kemudian karena fungsi Anda tampaknya tidak ditemukan, jika Anda tidak memiliki pembungkus extern "C" di sekitar deklarasi dan header disertakan dalam campuran sumber C dan C ++.
Salah satu alasan untuk mencegah orang menggunakan kompilasi C sebagai pengaturan C ++ adalah karena ini berarti kode sumber mereka tidak lagi portabel. Pengaturan itu adalah pengaturan proyek dan jadi jika file .c jatuh ke proyek lain, itu tidak akan dikompilasi sebagai c ++. Saya lebih suka orang meluangkan waktu untuk mengubah nama sufiks file menjadi .cpp.
sumber
Ini tentang ABI, untuk memungkinkan aplikasi C dan C ++ menggunakan antarmuka C tanpa masalah.
Karena bahasa C sangat mudah, pembuatan kode stabil selama bertahun-tahun untuk berbagai kompiler, seperti GCC, Borland C \ C ++, MSVC dll.
Sementara C ++ menjadi semakin populer, banyak hal yang harus ditambahkan ke dalam domain C ++ baru (misalnya akhirnya Cfront ditinggalkan di AT&T karena C tidak dapat mencakup semua fitur yang dibutuhkan). Seperti fitur templat , dan pembuatan kode waktu kompilasi, dari masa lalu, berbagai vendor kompiler benar-benar melakukan implementasi aktual dari kompiler dan tautan C ++ secara terpisah, ABI yang sebenarnya tidak kompatibel sama sekali dengan program C ++ pada platform yang berbeda.
Orang-orang mungkin masih ingin mengimplementasikan program yang sebenarnya dalam C ++ tetapi tetap menggunakan antarmuka C lama dan ABI seperti biasa, file header harus menyatakan extern "C" {} , ini memberitahu kompiler menghasilkan C ABI yang kompatibel / lama / sederhana / mudah. untuk fungsi antarmuka jika kompiler adalah kompiler C bukan kompiler C ++.
sumber