The C preprocessor adalah dibenarkan ditakuti dan dijauhi oleh C ++ masyarakat. Fungsi in-lined, const dan templat biasanya merupakan alternatif yang lebih aman dan unggul untuk a #define
.
Makro berikut:
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
sama sekali tidak lebih unggul dari tipe safe:
inline bool succeeded(int hr) { return hr >= 0; }
Tetapi makro memang memiliki tempatnya, harap cantumkan penggunaan yang Anda temukan untuk makro yang tidak dapat Anda lakukan tanpa preprocessor.
Silakan masukkan setiap kasus penggunaan dalam jawaban yang terpisah sehingga dapat dipilih dan jika Anda tahu cara mencapai salah satu jawaban tanpa preprosessor tunjukkan bagaimana dalam komentar jawaban itu.
c++
c-preprocessor
Motti
sumber
sumber
Jawaban:
Sebagai pembungkus untuk fungsi debug, untuk secara otomatis lulus hal-hal seperti
__FILE__
,__LINE__
, dll:sumber
__FILE__
dan__LINE__
juga memerlukan preprocessor. Menggunakannya dalam kode Anda seperti vektor infeksi untuk preprosesor.Metode harus selalu lengkap, kode dapat dikompilasi; makro mungkin berupa fragmen kode. Dengan demikian Anda dapat mendefinisikan makro foreach:
Dan gunakan sebagai berikut:
Sejak C ++ 11, ini digantikan oleh range-based untuk loop .
sumber
for_each
adalah hal yang buruk, karena kode setiap elemen dijalankan tidak lokal ke titik panggilan.foreach
, (dan saya sangat merekomendasikanBOOST_FOREACH
daripada solusi linting tangan) mari Anda menjaga kode dekat dengan situs iterasi, membuatnya lebih mudah dibaca. Konon, begitu lambda diluncurkan,for_each
mungkin sekali lagi jalan yang harus ditempuh.Pelindung file header mengharuskan makro.
Apakah ada area lain yang membutuhkan makro? Tidak banyak (jika ada).
Apakah ada situasi lain yang mendapat manfaat dari makro? IYA!!!
Satu tempat saya menggunakan macro adalah dengan kode yang sangat berulang. Misalnya, ketika membungkus kode C ++ untuk digunakan dengan antarmuka lain (.NET, COM, Python, dll ...), saya perlu menangkap berbagai jenis pengecualian. Begini cara saya melakukannya:
Saya harus meletakkan tangkapan ini di setiap fungsi pembungkus. Daripada mengetik blok tangkapan penuh setiap kali, saya cukup mengetik:
Ini juga membuat perawatan lebih mudah. Jika saya harus menambahkan tipe pengecualian baru, hanya ada satu tempat yang perlu saya tambahkan.
Ada juga contoh berguna lainnya: banyak di antaranya termasuk makro
__FILE__
dan__LINE__
preprosesor.Bagaimanapun, makro sangat berguna jika digunakan dengan benar. Makro tidak jahat - penyalahgunaannya adalah jahat.
sumber
#pragma once
hari ini, jadi saya ragu penjaga benar-benar diperlukan#pragma once
istirahat pada banyak sistem build umum.void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }
. Dan di sisi fungsi:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
Kebanyakan:
__LINE__
dan__FILE__
)sumber
Di dalam kompilasi bersyarat, untuk mengatasi masalah perbedaan antara kompiler:
sumber
close
fungsi atau metode. Kemudian ketika Anda menyertakan header perpustakaan ini dan header dengan makro ini daripada Anda memiliki masalah besar, Anda tidak dapat menggunakan API perpustakaan.#ifdef WE_ARE_ON_WIN32
plz :)Saat Anda ingin membuat string dari ekspresi, contoh terbaik untuk ini adalah
assert
(#x
mengubah nilaix
menjadi string).sumber
Konstanta string terkadang lebih baik didefinisikan sebagai makro karena Anda dapat melakukan lebih banyak dengan string literal daripada dengan a
const char *
.mis. String literal dapat dengan mudah digabungkan .
Jika a
const char *
digunakan maka semacam kelas string harus digunakan untuk melakukan penggabungan saat runtime:sumber
Ketika Anda ingin mengubah aliran program (
return
,break
dancontinue
) kode dalam suatu fungsi berperilaku berbeda dari kode yang sebenarnya diuraikan dalam fungsi.sumber
-1
atauNULL
. Jadi makro dapat sangat mengurangi kode boilerplate di sana.Yang jelas termasuk penjaga
sumber
Anda tidak dapat melakukan hubungan pendek argumen panggilan fungsi menggunakan panggilan fungsi biasa. Sebagai contoh:
sumber
Katakanlah kita akan mengabaikan hal-hal yang jelas seperti pelindung tajuk.
Terkadang, Anda ingin membuat kode yang perlu disalin / ditempelkan oleh precompiler:
yang memungkinkan Anda untuk kode ini:
Dan dapat menghasilkan pesan seperti:
Perhatikan bahwa mencampur template dengan makro dapat menghasilkan hasil yang lebih baik (yaitu secara otomatis menghasilkan nilai-nilai berdampingan dengan nama variabel mereka)
Di lain waktu, Anda memerlukan __FILE__ dan / atau __LINE__ dari beberapa kode, untuk menghasilkan info debug, misalnya. Berikut ini adalah klasik untuk Visual C ++:
Seperti dengan kode berikut:
itu menghasilkan pesan seperti:
Di lain waktu, Anda perlu membuat kode menggunakan operator gabungan # dan ##, seperti menghasilkan getter dan setter untuk properti (ini untuk kasus yang cukup terbatas, sampai).
Di lain waktu, Anda akan menghasilkan kode yang tidak dapat dikompilasi jika digunakan melalui fungsi, seperti:
Yang bisa digunakan sebagai
(masih, saya hanya melihat kode semacam ini benar digunakan sekali )
Terakhir, namun tidak kalah pentingnya, yang terkenal
boost::foreach
!!!(Catatan: kode salin / tempel dari beranda boost)
Yang (IMHO) jauh lebih baik daripada
std::for_each
.Jadi, makro selalu berguna karena mereka berada di luar aturan kompiler normal. Tapi saya menemukan bahwa sebagian besar waktu saya melihat satu, mereka secara efektif tetap kode C tidak pernah diterjemahkan ke dalam C ++ yang tepat.
sumber
#include <sstream> #include <iostream> using namespace std; void trace(char const * file, int line, ostream & o) { cerr<<file<<":"<<line<<": "<< static_cast<ostringstream & >(o).str().c_str()<<endl; } struct Oss { ostringstream s; ostringstream & lval() { return s; } }; #define TRACE(ostreamstuff) trace(__FILE__, __LINE__, Oss().lval()<<ostreamstuff) int main() { TRACE("Hello " << 123); return 0; }
Dengan begitu, makro jauh lebih pendek.Kerangka uji unit untuk C ++ seperti UnitTest ++ cukup banyak berputar di sekitar macro preprocessor. Beberapa baris kode tes unit berkembang menjadi hierarki kelas yang tidak menyenangkan untuk mengetik secara manual. Tanpa sesuatu seperti UnitTest ++ dan itu sihir preprosesor, saya tidak tahu bagaimana Anda akan secara efisien menulis tes unit untuk C ++.
sumber
Takut preprocessor C seperti takut pada lampu pijar hanya karena kita mendapatkan lampu neon. Ya, yang pertama bisa {listrik | waktu programmer} tidak efisien. Ya, Anda bisa terbakar (secara harfiah) oleh mereka. Tetapi mereka bisa menyelesaikan pekerjaan jika Anda menanganinya dengan benar.
Saat Anda memprogram sistem tertanam, C gunakan untuk menjadi satu-satunya opsi selain form assembler. Setelah memprogram di desktop dengan C ++ dan kemudian beralih ke target yang lebih kecil dan disematkan, Anda belajar untuk berhenti mengkhawatirkan "ketidakberesan" dari begitu banyak fitur C telanjang (termasuk makro) dan hanya mencoba mencari tahu penggunaan terbaik dan aman yang bisa Anda dapatkan dari mereka fitur.
Alexander Stepanov mengatakan :
sumber
Kami menggunakan
__FILE__
dan__LINE__
makro untuk tujuan diagnostik dalam pengecualian kaya informasi melempar, menangkap dan mencatat, bersama dengan pemindai file log otomatis dalam infrastruktur QA kami.Misalnya, makro melempar
OUR_OWN_THROW
dapat digunakan dengan tipe pengecualian dan parameter konstruktor untuk pengecualian itu, termasuk deskripsi tekstual. Seperti ini:Makro ini tentu saja akan melempar
InvalidOperationException
pengecualian dengan deskripsi sebagai parameter konstruktor, tetapi juga akan menulis pesan ke file log yang terdiri dari nama file dan nomor baris tempat lemparan terjadi dan deskripsi tekstualnya. Pengecualian yang dilemparkan akan mendapatkan id, yang juga akan dicatat. Jika pengecualian pernah tertangkap di tempat lain dalam kode, itu akan ditandai seperti itu dan file log kemudian akan menunjukkan bahwa pengecualian spesifik telah ditangani dan karena itu tidak mungkin menjadi penyebab kecelakaan yang mungkin akan dicatat nanti. Pengecualian yang tidak ditangani dapat dengan mudah diambil oleh infrastruktur QA otomatis kami.sumber
Pengulangan kode.
Coba lihat untuk meningkatkan perpustakaan preprocessor , ini semacam meta-meta-programming. Dalam topik-> motivasi Anda dapat menemukan contoh yang baik.
sumber
Beberapa hal yang sangat canggih dan berguna masih dapat dibangun menggunakan preprocessor (macro), yang Anda tidak akan pernah bisa menggunakan c ++ "konstruksi bahasa" termasuk templat.
Contoh:
Membuat sesuatu sebagai pengidentifikasi C dan sebuah string
Cara mudah untuk menggunakan variabel tipe enum sebagai string dalam C
Tingkatkan Preprocessor Metaprogramming
sumber
stdio.h
dansal.h
masukkan filevc12
untuk lebih memahami.Saya kadang-kadang menggunakan makro sehingga saya dapat mendefinisikan informasi di satu tempat, tetapi menggunakannya dengan cara yang berbeda di berbagai bagian kode. Itu hanya sedikit jahat :)
Misalnya, di "field_list.h":
Kemudian untuk enum publik dapat didefinisikan hanya menggunakan nama:
Dan dalam fungsi init pribadi, semua bidang dapat digunakan untuk mengisi tabel dengan data:
sumber
Salah satu penggunaan yang umum adalah untuk mendeteksi lingkungan kompilasi, untuk pengembangan lintas platform Anda dapat menulis satu set kode untuk linux, misalnya, dan lainnya untuk windows ketika tidak ada perpustakaan lintas platform yang sudah ada untuk keperluan Anda.
Jadi, dalam contoh kasar, sebuah cross-platform mutex dapat dimiliki
Untuk fungsi, mereka berguna ketika Anda ingin secara eksplisit mengabaikan keamanan jenis. Seperti banyak contoh di atas dan di bawah ini untuk melakukan ASSERT. Tentu saja, seperti banyak fitur C / C ++ Anda dapat menembak diri sendiri, tetapi bahasa memberi Anda alat dan memungkinkan Anda memutuskan apa yang harus dilakukan.
sumber
Sesuatu seperti
Sehingga Anda bisa saja misalnya punya
dan dapatkan nama file sumber dan nomor baris masalah yang dicetak ke log Anda jika n salah.
Jika Anda menggunakan panggilan fungsi normal seperti
alih-alih makro, yang bisa Anda dapatkan adalah nomor baris fungsi tegas Anda dicetak ke log, yang akan kurang bermanfaat.
sumber
<cassert>
paraassert()
makro, yang kesedihan file / line / fungsi Info? (dalam semua implementasi yang saya lihat, sih)Tidak seperti solusi template 'lebih disukai' yang dibahas dalam utas saat ini, Anda dapat menggunakannya sebagai ekspresi konstan:
sumber
template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
Anda dapat menggunakan #defines untuk membantu debugging dan skenario pengujian unit. Misalnya, buat varian pencatatan khusus dari fungsi memori dan buat memlog_preinclude.h khusus:
Kompilasi kode Anda menggunakan:
Tautan di memlog.o Anda ke gambar akhir. Anda sekarang mengontrol malloc, dll, mungkin untuk tujuan logging, atau untuk mensimulasikan kegagalan alokasi untuk tes unit.
sumber
Ketika Anda membuat keputusan pada waktu kompilasi atas perilaku spesifik Compiler / OS / Hardware.
Hal ini memungkinkan Anda untuk membuat antarmuka Anda ke fitur spesifik Comppiler / OS / Hardware.
sumber
Saya menggunakan makro untuk dengan mudah mendefinisikan Pengecualian:
di mana DEF_EXCEPTION berada
sumber
Compiler dapat menolak permintaan Anda untuk inline.
Makro akan selalu memiliki tempat mereka.
Sesuatu yang saya temukan bermanfaat adalah #define DEBUG untuk penelusuran debug - Anda dapat membiarkannya 1 saat men-debug masalah (atau bahkan membiarkannya selama seluruh siklus pengembangan) kemudian matikan saat saatnya untuk mengirim.
sumber
Dalam pekerjaan terakhir saya, saya sedang mengerjakan pemindai virus. Untuk mempermudah saya melakukan debug, saya memiliki banyak logging yang macet di semua tempat, tetapi dalam aplikasi permintaan tinggi seperti itu, biaya pemanggilan fungsi terlalu mahal. Jadi, saya datang dengan Makro kecil ini, yang masih memungkinkan saya mengaktifkan debug logging pada versi rilis di situs pelanggan, tanpa biaya panggilan fungsi akan memeriksa flag debug dan hanya kembali tanpa mencatat apa pun, atau jika diaktifkan , akan melakukan pencatatan ... Makro didefinisikan sebagai berikut:
Karena VA_ARGS dalam fungsi log, ini adalah kasus yang bagus untuk makro seperti ini.
Sebelum itu, saya menggunakan makro dalam aplikasi keamanan tinggi yang perlu memberi tahu pengguna bahwa mereka tidak memiliki akses yang benar, dan itu akan memberi tahu mereka bendera apa yang mereka butuhkan.
Makro didefinisikan sebagai:
Lalu, kami bisa memercikkan cek di seluruh UI, dan itu akan memberi tahu Anda peran mana yang diizinkan untuk melakukan tindakan yang Anda coba lakukan, jika Anda belum memiliki peran itu. Alasan dua dari mereka adalah untuk mengembalikan nilai di beberapa tempat, dan kembali dari fungsi batal di tempat lain ...
Ngomong-ngomong, begitulah cara saya menggunakannya, dan saya tidak yakin bagaimana ini bisa membantu dengan template ... Selain itu, saya mencoba untuk menghindarinya, kecuali BENAR-BENAR diperlukan.
sumber
Satu lagi makro pendahuluan. T: jenis, c: wadah, i: iterator
Penggunaan (menunjukkan konsep, tidak nyata):
Implementasi yang lebih baik tersedia: Google "BOOST_FOREACH"
Artikel bagus tersedia: Cinta Bersyarat: FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html
sumber
Mungkin penggunaan greate makro dalam pengembangan platform-independen. Pikirkan tentang kasus tipe inkonsistensi - dengan makro, Anda cukup menggunakan file header yang berbeda - seperti: --WIN_TYPES.H
--POSIX_TYPES.h
--program.h
Jauh lebih mudah dibaca daripada mengimplementasikannya dengan cara lain, menurut saya.
sumber
Tampaknya VA_ARGS hanya disebutkan secara tidak langsung sejauh ini:
Saat menulis kode C ++ 03 generik, dan Anda memerlukan sejumlah variabel (generik) parameter, Anda bisa menggunakan makro alih-alih templat.
Catatan: Secara umum, pemeriksaan nama / lemparan juga dapat dimasukkan ke dalam
get_op_from_name
fungsi hipotetis . Ini hanya sebuah contoh. Mungkin ada kode generik lain yang mengelilingi panggilan VA_ARGS.Setelah kita mendapatkan templat variadic dengan C ++ 11, kita bisa menyelesaikan ini dengan benar dengan templat.
sumber
Saya pikir trik ini adalah penggunaan yang cerdas dari preprocessor yang tidak dapat ditiru dengan suatu fungsi:
Maka Anda dapat menggunakannya seperti ini:
Anda juga dapat mendefinisikan makro RELEASE_ONLY.
sumber
Anda bisa
#define
konstanta pada baris perintah kompiler menggunakan opsi-D
atau/D
. Ini sering berguna ketika mengkompilasi silang perangkat lunak yang sama untuk beberapa platform karena Anda dapat membuat makefile Anda mengontrol konstanta apa yang didefinisikan untuk setiap platform.sumber