Katakanlah kita memiliki basis kode yang digunakan untuk banyak pelanggan yang berbeda, dan kami memiliki beberapa kode di dalamnya yang relevan hanya untuk pelanggan tipe X. Apakah lebih baik menggunakan arahan preprosesor untuk memasukkan kode ini hanya pada pelanggan tipe X, atau untuk menggunakan pernyataan if? Agar lebih jelas:
// some code
#if TYPE_X_COSTUMER = 1
// do some things
#endif
// rest of the code
atau
if(TYPE_X_COSTUMER) {
// do some things
}
Argumen yang bisa saya pikirkan adalah:
- Arahan preprosesor menghasilkan jejak kode yang lebih kecil dan lebih sedikit cabang (pada kompiler yang tidak mengoptimalkan)
- Jika pernyataan dihasilkan dengan kode yang selalu dikompilasi, misalnya jika seseorang akan membuat kesalahan yang akan merusak kode yang tidak relevan untuk proyek yang dikerjakannya, kesalahan akan tetap muncul, dan ia tidak akan merusak basis kode. Kalau tidak, dia tidak akan menyadari korupsi.
- Saya selalu diberitahu untuk memilih penggunaan prosesor daripada penggunaan preprocessor (Jika ini argumen sama sekali ...)
Apa yang lebih disukai - ketika berbicara tentang basis kode untuk banyak pelanggan yang berbeda?
TYPE_X_CUSTOMER
masih makro preprosesor?Jawaban:
Saya pikir ada satu keuntungan menggunakan #define yang tidak Anda sebutkan, dan itu adalah fakta bahwa Anda dapat mengatur nilai pada baris perintah (dengan demikian, atur dari skrip build satu langkah Anda).
Selain itu, umumnya lebih baik menghindari makro. Mereka tidak menghargai pelingkupan dan yang dapat menyebabkan masalah. Hanya kompiler yang sangat bodoh yang tidak dapat mengoptimalkan kondisi berdasarkan konstanta waktu kompilasi. Saya tidak tahu apakah itu merupakan masalah bagi produk Anda (misalnya mungkin penting untuk menjaga kode kecil pada platform tertanam).
sumber
Adapun banyak pertanyaan, jawaban dari pertanyaan ini tergantung . Alih-alih mengatakan mana yang lebih baik saya lebih suka memberikan contoh dan tujuan di mana yang satu lebih baik dari yang lain.
Baik preprosesor maupun konstanta memiliki tempat sendiri untuk penggunaan yang sesuai.
Dalam hal pra-prosesor, kode dihapus sebelum waktu kompilasi. Oleh karena itu, paling cocok untuk situasi di mana kode diharapkan tidak dikompilasi . Ini dapat memengaruhi struktur modul, dependensi, dan ini memungkinkan pemilihan segmen kode terbaik untuk aspek kinerja. Dalam kasus-kasus berikut, seseorang harus membagi kode hanya dengan menggunakan preprosesor.
Kode multi-platform:
Misalnya, ketika kode dikompilasi di bawah platform yang berbeda, ketika kode memiliki ketergantungan pada nomor versi OS tertentu (atau bahkan versi kompiler - meskipun ini sangat jarang). Sebagai contoh ketika Anda berurusan dengan rekan-rekan kecil-endien kode besar-endien - mereka harus dipisahkan dengan preprocessor daripada konstanta. Atau jika Anda mengkompilasi kode untuk Windows juga Linux dan panggilan sistem tertentu sangat berbeda.
Patch Eksperimental:
Kasus lain yang membuat ini dibenarkan adalah beberapa kode eksperimental yang berisiko atau modul utama tertentu yang perlu dihilangkan yang akan memiliki hubungan signifikan atau perbedaan kinerja. Alasan mengapa seseorang ingin menonaktifkan kode melalui preprocessor daripada bersembunyi di bawah if () adalah karena kami mungkin tidak yakin bug yang diperkenalkan oleh set perubahan khusus ini dan kami berjalan di bawah dasar eksperimental. Jika gagal, kita harus melakukan apa pun selain menonaktifkan kode itu dalam produksi selain menulis ulang. Beberapa waktu yang ideal untuk digunakan
#if 0
untuk mengomentari seluruh kode.Berurusan dengan dependensi:
Alasan lain di mana Anda mungkin ingin membuat Sebagai contoh, jika Anda tidak ingin mendukung gambar JPEG, Anda dapat membantu menyingkirkan mengkompilasi modul / rintisan itu dan akhirnya perpustakaan tidak akan (secara statis atau dinamis) menautkan itu modul. Kadang-kadang paket dijalankan
./configure
untuk mengidentifikasi ketersediaan ketergantungan tersebut dan jika perpustakaan tidak ada, (atau pengguna tidak ingin mengaktifkan) fungsionalitas tersebut dinonaktifkan secara otomatis tanpa menghubungkan dengan perpustakaan itu. Ini selalu bermanfaat jika arahan ini dibuat secara otomatis.Perizinan:
Salah satu contoh contoh preprocessor directive yang sangat menarik adalah ffmpeg . Ini memiliki codec yang berpotensi melanggar paten dengan penggunaannya. Jika Anda mengunduh sumber dan kompilasi untuk menginstal, ia menanyakan apakah Anda ingin atau menyimpan hal-hal seperti itu. Menyembunyikan kode di bawah beberapa jika kondisi masih bisa mendarat Anda di pengadilan!
Copy-paste kode:
Aka macro. Ini bukan saran untuk menggunakan makro secara berlebihan - hanya saja makro memiliki cara yang jauh lebih kuat untuk menerapkan padanan salinan di masa lalu . Tapi gunakan dengan hati-hati; dan gunakan jika Anda tahu apa yang Anda lakukan. Konstanta tentu saja tidak dapat melakukan ini. Tetapi seseorang dapat menggunakan
inline
fungsi juga jika itu mudah dilakukan.Jadi kapan Anda menggunakan konstanta?
Hampir di tempat lain.
Aliran kode yang lebih rapi:
Secara umum, ketika Anda menggunakan konstanta, hampir tidak dapat dibedakan dari variabel reguler dan karenanya, itu adalah kode yang lebih mudah dibaca. Jika Anda menulis rutin yaitu 75 baris - memiliki 3 atau 4 baris setelah setiap 10 baris dengan #ifdef SANGAT tidak dapat dibaca . Mungkin diberikan konstanta primer yang diatur oleh #ifdef, dan menggunakannya dalam aliran alami di mana saja.
Kode yang terindentasi dengan baik: Semua arahan preprosesor, tidak pernah bekerja dengan baik dengan kode yang diindentasi dengan baik . Bahkan jika Anda compiler tidak memungkinkan lekukan dari #def, Pre-ANSI C preprocessor tidak memungkinkan untuk ruang antara awal baris dan "#" karakter; "#" yang memimpin harus selalu ditempatkan di kolom pertama.
Konfigurasi:
Alasan lain mengapa konstanta / atau variabel masuk akal adalah bahwa mereka dapat dengan mudah berevolusi dari yang tertaut ke global atau di masa depan dapat diperluas untuk diturunkan dari file konfigurasi.
Satu hal terakhir:
Jangan pernah gunakan arahan preprocessor
#ifdef
untuk#endif
melintasi ruang lingkup atau{ ... }
. yaitu mulai#ifdef
atau berakhir#endif
pada sisi yang berbeda{ ... }
. Ini sangat buruk; itu bisa membingungkan, terkadang berbahaya.Ini tentu saja, bukan daftar lengkap, tetapi menunjukkan Anda perbedaan nyata, di mana metode mana yang lebih tepat untuk digunakan. Ini tidak benar-benar tentang mana yang lebih baik , itu selalu lebih mana yang lebih alami untuk digunakan dalam konteks tertentu.
sumber
Apakah pelanggan Anda memiliki akses ke kode Anda? Jika ya, maka preprosesor bisa menjadi pilihan yang lebih baik. Kami memiliki sesuatu yang serupa dan kami menggunakan flag waktu kompilasi untuk berbagai pelanggan (atau fitur khusus pelanggan). Kemudian skrip dapat mengekstrak kode khusus pelanggan dan kami mengirimkan kode itu. Pelanggan tidak mengetahui pelanggan lain atau fitur khusus pelanggan lainnya.
sumber
Beberapa poin tambahan yang layak disebutkan:
Compiler dapat memproses beberapa jenis ekspresi konstan yang preprocessor tidak dapat. Sebagai contoh, preprocessor umumnya tidak memiliki cara untuk mengevaluasi
sizeof()
pada tipe non-primitif. Ini mungkin memaksa penggunaanif()
dalam beberapa kasus.Kompiler akan mengabaikan sebagian besar masalah sintaksis dalam kode yang dilompati
#if
(beberapa masalah terkait preprosesor masih dapat menyebabkan masalah) tetapi bersikeras pada kebenaran sintaksis untuk kode yang dilewatiif()
. Jadi, jika kode yang dilompati untuk beberapa build tetapi tidak yang lain menjadi tidak valid sebagai akibat dari perubahan di tempat lain dalam file (mis. Pengidentifikasi yang diganti nama) itu umumnya akan mengomel pada semua build jika dinonaktifkan denganif()
tetapi tidak jika dinonaktifkan oleh#if
. Tergantung pada mengapa kode dilewati, itu mungkin atau mungkin bukan hal yang baik.Beberapa kompiler akan menghilangkan kode yang tidak terjangkau sementara yang lain tidak; deklarasi durasi penyimpanan statis dalam kode yang tidak dapat dijangkau, termasuk string literal, dapat mengalokasikan ruang meskipun tidak ada kode yang dapat menggunakan ruang yang dialokasikan demikian.
Makro dapat menggunakan
if()
tes di dalamnya, tetapi sayangnya tidak ada mekanisme bagi preprosesor untuk melakukan logika kondisional dalam makro [perhatikan bahwa untuk digunakan dalam makro,? :
operator mungkin sering lebih baik daripadaif
, tetapi prinsip yang sama berlaku].The
#if
direktif dapat digunakan untuk mengontrol macro apa yang bisa didefinisikan.Saya pikir
if()
seringkali lebih bersih untuk sebagian besar kasus kecuali yang melibatkan pengambilan keputusan berdasarkan apa yang digunakan kompiler, atau makro apa yang harus didefinisikan; beberapa masalah di atas mungkin mengharuskan penggunaan#if
, meskipun demikian, bahkan dalam kasus di manaif
akan tampak lebih bersih.sumber
Singkat cerita: kekhawatiran tentang arahan preprosesor pada dasarnya adalah 2, beberapa inklusi dan mungkin kode yang lebih mudah dibaca dan logika yang lebih criptic.
Jika proyek Anda kecil dengan hampir tidak ada rencana besar untuk masa depan, saya pikir kedua opsi menawarkan rasio yang sama antara pro dan kontra, tetapi jika proyek Anda akan sangat besar, pertimbangkan hal lain seperti penulisan file header terpisah jika Anda masih ingin menggunakan arahan preprosesor, tetapi perlu diingat bahwa pendekatan semacam ini biasanya membuat kode kurang mudah dibaca dan pastikan bahwa nama konstanta itu berarti sesuatu untuk logika bisnis dari program itu sendiri.
sumber