Jadi saya sedang mengerjakan desain perangkat lunak menggunakan C untuk prosesor tertentu. Tool-kit termasuk kemampuan untuk mengkompilasi C serta C ++. Untuk apa yang saya lakukan, tidak ada alokasi memori dinamis yang tersedia di lingkungan ini dan program ini secara keseluruhan cukup sederhana. Belum lagi bahwa perangkat ini hampir tidak memiliki daya atau sumber daya prosesor. Benar-benar tidak ada kebutuhan kuat untuk menggunakan C ++ apa pun.
Yang sedang berkata, ada beberapa tempat di mana saya melakukan fungsi overloading (fitur C ++). Saya perlu mengirim beberapa jenis data yang berbeda dan tidak merasa ingin menggunakan printf
pemformatan gaya dengan semacam %s
argumen (atau apa pun). Saya telah melihat beberapa orang yang tidak memiliki akses ke kompiler C ++ melakukan printf
hal itu, tetapi dalam kasus saya dukungan C ++ tersedia.
Sekarang saya yakin saya mungkin mendapatkan pertanyaan mengapa saya harus membebani fungsi untuk memulai. Jadi saya akan mencoba menjawabnya sekarang. Saya perlu mengirimkan berbagai jenis data keluar dari port serial sehingga saya memiliki beberapa kelebihan yang mengirimkan tipe data berikut:
unsigned char*
const char*
unsigned char
const char
Saya lebih suka tidak memiliki satu metode yang menangani semua hal ini. Ketika saya memanggil fungsi saya hanya ingin mengirimkan port serial, saya tidak punya banyak sumber daya jadi saya tidak ingin melakukan APA SAJA selain transmisi saya.
Orang lain melihat program saya dan bertanya, "mengapa Anda menggunakan file CPP?" Jadi, itulah satu-satunya alasan saya. Apakah itu praktik buruk?
Memperbarui
Saya ingin menjawab beberapa pertanyaan yang diajukan:
Jawaban obyektif untuk dilema Anda akan tergantung pada:
Apakah ukuran yang dapat dieksekusi tumbuh secara signifikan jika Anda menggunakan C ++.
Pada saat ini ukuran executable mengkonsumsi 4.0% dari memori program (dari 5248 bytes) dan 8,3% dari memori data (dari 342 bytes). Yaitu, mengkompilasi untuk C ++ ... Saya tidak tahu seperti apa tampilannya untuk C karena saya belum pernah menggunakan kompiler C. Saya tahu bahwa program ini tidak akan tumbuh lagi, jadi untuk seberapa terbatas sumber daya saya akan mengatakan saya baik-baik saja di sana ...
Apakah ada dampak negatif yang nyata pada kinerja jika Anda menggunakan C ++.
Ya kalau ada, saya belum melihat apa-apa ... tapi sekali lagi itu sebabnya saya menanyakan pertanyaan ini karena saya tidak sepenuhnya mengerti.
Apakah kode dapat digunakan kembali pada platform yang berbeda di mana hanya kompiler C yang tersedia.
Saya tahu bahwa jawabannya adalah tidak . Kami sebenarnya mempertimbangkan untuk pindah ke prosesor yang berbeda, tetapi hanya prosesor berbasis ARM yang lebih kuat (yang semuanya saya tahu sebenarnya memiliki rantai alat penyusun C ++).
//
komentar. Jika berhasil, mengapa tidak?//
komentar telah dalam standar C sejak C99.Jawaban:
Aku tidak akan pergi sejauh untuk menyebutnya "praktik buruk" per se , tetapi tidak aku yakin itu benar-benar solusi yang tepat untuk masalah Anda. Jika yang Anda inginkan adalah empat fungsi terpisah untuk melakukan empat tipe data Anda, mengapa tidak melakukan apa yang telah dilakukan programmer C sejak dahulu kala:
Itu efektif apa yang dilakukan oleh kompiler C ++ di belakang layar dan itu bukan biaya overhead yang besar untuk programmer. Hindari semua masalah "mengapa Anda menulis tidak-cukup-C dengan kompiler C ++", dan berarti tidak ada orang lain di proyek Anda yang akan bingung dengan bit C ++ mana yang "diizinkan" dan bit mana yang tidak.
sumber
#define
mengirimkan () menggunakan C11_Generic
Hanya menggunakan beberapa fitur C ++ sementara memperlakukannya sebagai C tidak umum, tetapi juga tidak pernah terdengar. Bahkan, beberapa orang bahkan tidak menggunakan fitur sama sekali dari C ++, kecuali memeriksa jenis lebih ketat dan lebih kuat. Mereka hanya menulis C (berhati-hati menulis hanya di persimpangan umum C ++ dan C), kemudian kompilasi dengan kompiler C ++ untuk pengecekan tipe, dan kompiler C untuk pembuatan kode (atau tetap dengan kompiler C ++ sepanjang jalan).
Linux adalah contoh basis kode di mana orang-orang secara teratur meminta pengenal seperti
class
diubah namanya menjadiklass
ataukclass
, sehingga mereka dapat mengkompilasi Linux dengan kompiler C ++. Jelas, mengingat pendapat Linus tentang C ++ , mereka selalu tertembak :-D GCC adalah contoh basis kode yang pertama kali diubah menjadi "C ++ - clean C", dan kemudian secara bertahap refactored untuk menggunakan lebih banyak fitur C ++.Tidak ada yang salah dengan apa yang Anda lakukan. Jika Anda benar-benar paranoid tentang kualitas pembuatan kode kompiler C ++ Anda, Anda dapat menggunakan kompiler seperti Comeau C ++, yang mengkompilasi ke C sebagai bahasa target, dan kemudian menggunakan kompiler C. Dengan begitu, Anda juga dapat melakukan inspeksi langsung pada kode untuk melihat apakah menggunakan C ++ menyuntikkan kode sensitif kinerja yang tidak terduga. Itu tidak seharusnya menjadi kasus untuk overloading, meskipun, yang secara harfiah hanya secara otomatis menghasilkan fungsi yang berbeda bernama - TKI, persis apa yang akan Anda lakukan di C.
sumber
Jawaban obyektif untuk dilema Anda akan tergantung pada:
Jika jawaban untuk salah satu pertanyaan adalah "ya", Anda mungkin lebih baik membuat fungsi dengan nama berbeda untuk tipe data yang berbeda dan tetap menggunakan C.
Jika jawaban untuk semua pertanyaan adalah "tidak", saya tidak melihat alasan mengapa Anda tidak harus menggunakan C ++.
sumber
Anda mengajukan pertanyaan seolah-olah kompilasi dengan C ++ hanya memberi Anda akses ke lebih banyak fitur. Ini bukan kasusnya. Mengkompilasi dengan kompiler C ++ berarti bahwa kode sumber Anda ditafsirkan sebagai sumber C ++, dan C ++ adalah bahasa yang berbeda dari C. Keduanya memiliki subset umum yang cukup besar untuk diprogram dengan bermanfaat, tetapi masing-masing memiliki fitur yang kurang dimiliki oleh yang lain, dan itu adalah mungkin untuk menulis kode yang dapat diterima dalam kedua bahasa tetapi ditafsirkan berbeda.
Jika benar-benar yang Anda inginkan adalah fungsi kelebihan beban, maka saya benar-benar tidak melihat titik untuk membingungkan masalah dengan membawa C ++ ke dalamnya. Alih-alih fungsi yang berbeda dengan nama yang sama, yang dibedakan daftar parameternya, hanya menulis fungsi dengan nama yang berbeda.
Adapun kriteria objektif Anda,
Dapat dieksekusi mungkin sedikit lebih besar ketika dikompilasi sebagai C ++, relatif terhadap kode C serupa dibangun seperti itu. Minimal, C ++ executable akan memiliki manfaat meragukan C ++ name-mangling untuk semua nama fungsi, sejauh simbol apa pun disimpan dalam executable. (Dan itulah sebenarnya yang menyebabkan kelebihan Anda di tempat pertama.) Apakah perbedaannya cukup besar untuk menjadi penting bagi Anda adalah sesuatu yang harus Anda tentukan melalui eksperimen.
Saya ragu Anda akan melihat perbedaan kinerja nyata untuk kode yang Anda gambarkan vs analog murni-C hipotetis.
Saya akan memberikan sedikit perbedaan pada hal ini: jika Anda ingin menautkan kode yang Anda buat dengan C ++ ke kode lain , maka kode lain itu juga perlu ditulis dalam C ++ dan dibuat dengan C ++, atau Anda perlu untuk membuat ketentuan khusus dalam kode Anda sendiri (menyatakan tautan "C"), yang, selain itu, Anda tidak dapat melakukan sama sekali untuk fungsi kelebihan beban Anda. Di sisi lain, jika kode Anda ditulis dalam C dan dikompilasi sebagai C, maka orang lain dapat menghubungkannya dengan modul C dan C ++. Masalah seperti ini biasanya dapat diatasi dengan membalik tabel, tetapi karena Anda benar-benar tampaknya tidak membutuhkan C ++ maka mengapa menerima masalah seperti itu di tempat pertama?
sumber
Kembali pada hari itu, menggunakan kompiler C ++ sebagai “a better C” dipromosikan sebagai use case. Bahkan, awal C ++ persis seperti itu. Prinsip desain yang mendasarinya adalah Anda hanya dapat menggunakan fitur yang Anda inginkan dan tidak akan menimbulkan biaya fitur yang tidak Anda gunakan. Jadi, Anda dapat membebani fungsi (dengan menyatakan maksud melalui
overload
kata kunci!) Dan sisa proyek Anda tidak hanya akan mengkompilasi dengan baik tetapi akan menghasilkan kode yang tidak lebih buruk daripada yang dihasilkan oleh kompiler C.Sejak itu, bahasa-bahasa tersebut agak berbeda.
Setiap
malloc
kode C Anda akan menjadi kesalahan jenis ketidakcocokan - bukan masalah dalam kasus Anda tanpa memori dinamis! Demikian juga, semuavoid
petunjuk Anda di C akan membuat Anda tersandung, karena Anda harus menambahkan gips eksplisit. Tapi ... mengapa Anda melakukannya dengan cara itu ... Anda akan dibawa ke jalan menggunakan fitur C ++ lebih dan lebih.Jadi, mungkin saja dengan beberapa pekerjaan ekstra. Tapi itu akan berfungsi sebagai pintu gerbang ke adopsi C ++ skala besar. Dalam beberapa tahun orang-orang akan mengeluh tentang kode warisan Anda yang sepertinya ditulis pada tahun 1989, ketika mereka mengganti
malloc
panggilan Anda dengannew
, merobek blok kode badan loop untuk hanya memanggil algoritma saja, dan mencaci maki polimorfisme palsu yang tidak aman yang akan telah sepele seandainya kompiler diizinkan untuk melakukannya.Di sisi lain, Anda tahu bahwa semuanya akan sama jika Anda menuliskannya dalam C, jadi apakah salah untuk menulis dalam C daripada C ++ mengingat keberadaannya? Jika jawabannya "tidak", maka menggunakan fitur cherry-pick dari C ++ juga tidak salah .
sumber
Banyak bahasa pemrograman memiliki satu atau lebih budaya yang berkembang di sekitarnya, dan memiliki gagasan khusus tentang apa yang seharusnya menjadi "bahasa". Meskipun mungkin, praktis, dan bermanfaat untuk memiliki bahasa tingkat rendah yang cocok untuk pemrograman sistem yang menambah dialek C yang cocok untuk tujuan tersebut dengan beberapa fitur dari C ++ yang juga cocok, budaya di sekitar dua bahasa tidak khususnya tidak menyukai merger semacam itu.
Saya telah membaca tentang kompiler C tertanam yang menggabungkan beberapa fitur dari C ++, termasuk kemampuan untuk memilikinya
diartikan sebagai, pada dasarnya
serta kemampuan untuk membebani fungsi-fungsi statis (masalah pengubahan nama hanya muncul saat dieksporfungsi kelebihan beban). Tidak ada alasan mengapa kompiler C tidak dapat mendukung fungsionalitas seperti itu tanpa memaksakan persyaratan tambahan apa pun pada lingkungan penghubung dan jangka waktu, tetapi penolakan refleksif kompiler C terhadap hampir semua hal dari C ++ untuk menghindari beban lingkungan menyebabkan mereka menolak bahkan fitur yang tidak akan membebankan biaya seperti itu. Sementara itu, budaya C ++ memiliki sedikit minat pada lingkungan apa pun yang tidak dapat mendukung semua fitur bahasa itu. Kecuali atau sampai budaya C berubah untuk menerima fitur-fitur seperti itu, atau budaya C ++ berubah untuk mendorong implementasi himpunan bagian yang ringan, kode "hibrida" cenderung mengalami banyak keterbatasan dari kedua bahasa sementara terbatas pada kemampuannya untuk menuai manfaat beroperasi dalam superset gabungan.
Jika sebuah platform mendukung C dan C ++, kode library tambahan yang diperlukan untuk mendukung C ++ kemungkinan akan membuat executable agak lebih besar, meskipun efeknya tidak akan terlalu bagus. Eksekusi "fitur C" dalam C ++ kemungkinan tidak akan terlalu terpengaruh. Masalah yang lebih besar mungkin adalah bagaimana pengoptimal C dan C ++ menangani berbagai kasus sudut. Perilaku tipe data dalam C sebagian besar ditentukan oleh isi bit yang tersimpan di dalamnya, dan C ++ memiliki kategori struktur yang dikenal (PODS - Plain Old Data Structures) yang semantiknya juga sebagian besar ditentukan oleh bit yang disimpan. Baik standar C dan C ++, bagaimanapun, kadang-kadang memungkinkan perilaku yang bertentangan dengan yang tersirat oleh bit yang disimpan, dan pengecualian untuk perilaku "bit yang disimpan" berbeda antara kedua bahasa.
sumber
Faktor penting lainnya untuk dipertimbangkan: siapa pun yang akan mewarisi kode Anda.
Apakah orang itu akan selalu menjadi programmer C dengan kompiler C? Saya rasa begitu.
Apakah orang itu juga akan menjadi programmer C ++ dengan kompiler C ++? Saya ingin cukup yakin tentang ini sebelum membuat kode saya bergantung pada C ++ hal-hal tertentu.
sumber
Polimorfisme adalah fitur yang sangat bagus yang disediakan C ++ secara gratis. Namun, ada aspek lain yang mungkin perlu Anda pertimbangkan saat memilih kompiler. Ada alternatif untuk polimorfisme dalam C tetapi penggunaannya dapat menyebabkan alis terangkat. Ini adalah fungsi variadic, silakan merujuk ke tutorial fungsi Variadic .
Dalam kasus Anda itu akan menjadi sesuatu seperti
Saya suka pendekatan Phillips tapi itu mengotori perpustakaan Anda dengan banyak panggilan. Dengan antarmuka di atas bersih. Itu memang memiliki kekurangan dan pada akhirnya itu adalah masalah pilihan.
sumber
Arg_type_t
danvoid *
menunjuk ke data. Dengan mengatakan itu, ya, memberikan fungsi (tunggal) argumen yang menunjukkan tipe data memang merupakan alternatif yang layak.Untuk kasus khusus Anda yang secara statis kelebihan "fungsi" untuk beberapa jenis yang berbeda, Anda dapat mempertimbangkan hanya menggunakan C11 dengan mesin
_Generic
makro . Saya merasa itu mungkin cukup untuk kebutuhan Anda yang terbatas.Dengan menggunakan jawaban Philip Kendall Anda dapat mendefinisikan:
dan kode
transmit(foo)
apa pun jenisnyafoo
(di antara empat jenis yang tercantum di atas).Jika Anda hanya peduli GCC (dan kompatibel, misalnya dentang ) compiler, Anda bisa mempertimbangkan nya
__builtin_types_compatible_p
wtth nyatypeof
ekstensi.sumber
Sudut IMHO, ya, dan saya harus menjadi skizofrenia untuk menjawab yang satu ini karena saya suka kedua bahasa tetapi tidak ada hubungannya dengan efisiensi, tetapi lebih seperti keamanan dan penggunaan bahasa secara idiomatis.
Sisi C
Dari sudut pandang C, saya merasa sangat boros untuk membuat kode Anda membutuhkan C ++ hanya untuk menggunakan fungsi yang berlebihan. Kecuali jika Anda menggunakannya untuk polimorfisme statis dengan template C ++, itu adalah gula sintaksis sepele yang diperoleh sebagai ganti untuk beralih ke bahasa yang sama sekali berbeda. Lebih jauh lagi jika Anda ingin mengekspor fungsi Anda ke dylib (mungkin atau mungkin tidak menjadi masalah praktis), Anda tidak dapat lagi melakukannya dengan sangat praktis untuk konsumsi yang tersebar luas dengan semua lambang-lambang yang ditandai dengan nama.
Sisi C ++
Dari sudut pandang C ++, Anda seharusnya tidak menggunakan C ++ seperti C dengan fungsi yang berlebihan. Ini bukan dogmatisme gaya tetapi terkait dengan penggunaan praktis C ++ sehari-hari.
Jenis kode C normal Anda hanya masuk akal dan "aman" untuk ditulis jika Anda bekerja melawan sistem tipe C yang melarang hal-hal seperti copy ctors in
structs
. Setelah Anda bekerja di sistem tipe C ++ yang lebih kaya, fungsi sehari-hari yang bernilai sangat tinggi sepertimemset
danmemcpy
tidak menjadi fungsi yang harus Anda sandarkan sepanjang waktu. Alih-alih, itu adalah fungsi yang biasanya ingin Anda hindari seperti wabah, karena dengan tipe C ++, Anda tidak boleh memperlakukannya seperti bit dan byte mentah yang akan disalin dan diacak serta dibebaskan. Bahkan jika kode Anda hanya menggunakan hal-hal sepertimemset
pada primitif dan POD UDT saat ini, saat siapa pun menambahkan ctor ke UDT yang Anda gunakan (termasuk hanya menambahkan anggota yang memerlukannya, sepertistd::unique_ptr
member) terhadap fungsi-fungsi seperti itu atau fungsi virtual atau semacamnya, itu membuat semua kode C-style normal Anda rentan terhadap perilaku yang tidak terdefinisi. Ambillah dari Herb Sutter sendiri:Begitu banyak pengembang C yang tidak setuju dengan ini dan memang demikian, karena filosofi ini hanya berlaku jika Anda menulis kode dalam C ++. Anda kemungkinan besar sedang menulis kode yang sangat bermasalah jika Anda menggunakan fungsi seperti
memcpy
semua waktu dalam kode yang membangun sebagai C ++ , tapi itu baik-baik saja jika Anda melakukannya di C . Kedua bahasa ini sangat berbeda dalam hal ini karena perbedaan dalam sistem tipe. Sangat menggoda untuk melihat subset dari fitur yang dimiliki keduanya dan percaya satu dapat digunakan seperti yang lain, terutama pada sisi C ++, tetapi kode C + (atau kode C -) umumnya jauh lebih bermasalah daripada C dan Kode C ++.Demikian juga Anda tidak boleh menggunakan, katakanlah,
malloc
dalam konteks gaya-C (yang menyiratkan tidak ada EH) jika dapat memanggil fungsi C ++ secara langsung yang dapat dilemparkan, sejak itu Anda memiliki titik keluar implisit dalam fungsi Anda sebagai akibat dari pengecualian yang Anda tidak dapat menangkap kode C-style secara efektif, sebelum dapatfree
mengingatnya. Jadi, setiap kali Anda memiliki file yang membangun sebagai C ++ dengan.cpp
ekstensi atau apa pun dan tidak semua jenis hal-hal sepertimalloc
,memcpy
,memset
,qsort
, dll, maka ia meminta masalah lebih lanjut di telepon jika belum, kecuali jika itu adalah detail implementasi dari kelas yang hanya bekerja dengan tipe primitif, di mana pada saat itu masih perlu melakukan penanganan pengecualian untuk menjadi pengecualian aman. Jika Anda menulis kode C ++ Anda malah ingin umumnya mengandalkan RAII dan menggunakan hal-hal sepertivector
,unique_ptr
,shared_ptr
, dll, dan menghindari semua normal C-gaya pengkodean bila memungkinkan.Pengecualian-Keamanan
Sekali lagi, saat Anda berada di tanah C ++, orang akan mengharapkan kode Anda aman-pengecualian. Orang-orang mungkin mempertahankan kode Anda di masa mendatang, mengingat bahwa itu sudah ditulis dan dikompilasi dalam C ++, dan cukup gunakan
std::vector, dynamic_cast, unique_ptr, shared_ptr
, dll. Dalam kode yang disebut baik secara langsung atau tidak langsung oleh kode Anda, percaya itu tidak berbahaya karena kode Anda sudah "seharusnya" C ++ kode. Pada saat itu kita harus menghadapi kemungkinan bahwa segala sesuatu akan melempar, dan kemudian ketika Anda mengambil kode C baik-baik saja dan indah seperti ini:... sekarang rusak. Perlu ditulis ulang agar aman dari pengecualian:
Kotor! Itulah sebabnya sebagian besar pengembang C ++ akan menuntut ini:
Di atas adalah kode yang sesuai dengan pengecualian-aman RAII dari jenis yang umumnya disetujui pengembang C ++ karena fungsi tidak akan bocor pada baris kode mana yang memicu keluar tersirat sebagai akibat dari a
throw
.Pilih Bahasa
Anda harus merangkul sistem tipe C ++ dan filosofi dengan RAII, pengecualian-keselamatan, templat, OOP, dll. Atau merangkul C yang sebagian besar berputar di sekitar bit dan byte mentah. Anda seharusnya tidak membentuk perkawinan yang tidak suci antara kedua bahasa ini, dan sebaliknya memisahkan mereka ke dalam bahasa yang berbeda untuk diperlakukan dengan sangat berbeda alih-alih mengaburkan keduanya.
Bahasa-bahasa ini ingin menikahi Anda. Anda biasanya harus memilih satu daripada berkencan dan bermain-main dengan keduanya. Atau Anda bisa menjadi poligami seperti saya dan menikahi keduanya, tetapi Anda harus sepenuhnya mengubah pemikiran Anda ketika menghabiskan waktu dengan satu sama lain dan menjaga mereka terpisah satu sama lain sehingga mereka tidak saling bertarung.
Ukuran Biner
Hanya karena penasaran saya mencoba mengambil implementasi daftar bebas saya dan patokan sekarang dan porting ke C ++ karena saya benar-benar ingin tahu tentang ini:
... dan ingin tahu apakah ukuran biner akan mengembang sama sekali hanya sebagai C ++. Itu mengharuskan saya untuk menaburkan gips eksplisit di seluruh tempat yang jelek (salah satu alasan saya suka benar-benar menulis hal-hal tingkat rendah seperti pengalokasi dan struktur data dalam C lebih baik) tetapi hanya butuh satu menit.
Ini hanya membandingkan rilis rilis MSVC 64-bit untuk aplikasi konsol sederhana dan dengan kode yang tidak menggunakan fitur C ++, bahkan operator tidak kelebihan - hanya perbedaan antara membangunnya dengan C dan menggunakan, katakanlah,
<cstdlib>
alih-alih<stdlib.h>
dan hal-hal seperti itu, tetapi saya terkejut menemukan itu membuat perbedaan nol dengan ukuran biner!Biner adalah
9,728
byte ketika dibangun di C, dan juga9,278
byte saat dikompilasi sebagai kode C ++. Saya sebenarnya tidak mengharapkan itu. Saya pikir hal-hal seperti EH setidaknya akan menambah sedikit di sana (pikir itu setidaknya akan seperti seratus byte yang berbeda), meskipun mungkin itu bisa mengetahui bahwa tidak perlu menambahkan instruksi yang berhubungan dengan EH karena saya hanya menggunakan perpustakaan standar C dan tidak ada lemparan. Saya memikirkan sesuatuakan menambahkan sedikit ke ukuran biner bagaimanapun, seperti RTTI. Bagaimanapun, itu agak keren untuk melihat itu. Tentu saja saya tidak berpikir Anda harus menggeneralisasi dari hasil yang satu ini, tetapi setidaknya sedikit terkesan. Itu juga tidak berdampak pada tolok ukur, dan tentu saja, karena saya membayangkan ukuran biner yang identik juga berarti instruksi mesin yang dihasilkan identik.Yang mengatakan, siapa yang peduli tentang ukuran biner dengan masalah keselamatan dan teknik yang disebutkan di atas? Jadi sekali lagi, pilih bahasa dan raih filosofinya alih-alih mencoba untuk mengastastasinya; itu yang saya rekomendasikan.
sumber