Apakah menggunakan makro C / C ++ sebagai jalan pintas untuk kompilasi bersyarat merupakan praktik yang baik?

13

Katakanlah saya ingin memiliki beberapa jenis pesan keluaran dalam kode saya. Salah satunya adalah DEBUG, yang dicetak saja, ketika kode dikompilasi dalam mode Debug.

Biasanya saya harus menulis sesuatu seperti

#ifdef DEBUG
    std::cout << "Debug message" << std::endl;
#endif

yang cukup rumit dan menjengkelkan untuk digunakan di banyak tempat.

Apakah ini praktik yang baik untuk mendefinisikan makro untuk cuplikan kode, jadi Anda akan menggunakannya dengan cara ini?

MSG_DEBUG("Debug message")

Atau ada cara lain yang lebih elegan untuk menghadapinya tanpa makro? Saya tertarik tentang solusi yang mungkin baik dalam C dan C ++, karena saya menggunakan kedua bahasa dalam proyek yang berbeda.

Eenoku
sumber
Tidak jelas dari pertanyaan mengapa Anda tidak akan hanya meletakkan kode kondisional dalam fungsi dan menyebutnya. Apakah ada kendala lain yang akan mencegah hal itu?
Alex
@gnat Pertanyaan yang Anda sebutkan sangat luas, sehingga kebanyakan orang tidak akan menghubungkannya dengan topik ini, terutama ketika mereka akan mencari pertanyaan khusus ini di Internet.
Eenoku
3
Pertanyaan Anda ditandai dengan c dan c ++ , namun, itu bahasa yang sangat berbeda. Bisakah Anda mengklarifikasi yang mana yang Anda bicarakan? Contoh Anda akan baik-baik saja di C, tetapi mungkin lebih baik diimplementasikan dengan constexpr ifdi C ++, misalnya.
Jörg W Mittag
1
Sebagai tambahan, diagnostik harus dituju STDERR. Juga, mengapa tidak membuatnya bergantung NDEBUGseperti assert()tidak? Kemudian Anda bisa mendefinisikannya seperti #define DEBUG_MSG(MSG) assert(std::cerr << MSG), yang menguji kondisi aliran juga.
Deduplicator

Jawaban:

19

Tentu, jika Anda tidak keberatan menggunakan makro di tempat pertama, maka mendefinisikan yang diparameterisasi daripada terus mengulangi kode kondisional yang sama tentu lebih disukai dengan ukuran pengkodean yang baik.

Haruskah Anda menggunakan makro sama sekali? Dalam pandangan saya Anda harus, karena itu diterima praktik di C, dan solusi makro-kurang akan memerlukan setidaknya sesuatu dijalankan bahkan di luar mode debug. Pemrogram C biasanya akan memilih makro yang sedikit jelek daripada upaya run-time yang tidak perlu setiap saat.

Kilian Foth
sumber
13

Ada elemen preferensi pribadi di sini, tetapi di C ++ saya lebih suka melakukan ini di file header:

#ifdef _DEBUG
    void DebugMessage(...);
#else
    inline void DebugMessage(...) {}
#endif

Sehingga fungsi tersebut diuraikan dalam rilis build, tetapi merupakan fungsi yang tepat dalam debug build sehingga Anda dapat memiliki pemeriksaan tipe yang tepat, pesan kesalahan yang masuk akal, dll. Dan kemampuan untuk menambahkan fungsionalitas lebih lanjut (logging mungkin?) Nanti.

Jelas, Anda juga perlu membungkus definisi yang sesuai dari fungsi dalam .cppfile dalam suatu #ifdef _DEBUGblok.

Jack Aidley
sumber
Tetapi overhead panggilan masih ada di sini, bukan?
Eenoku
7
Jika kompiler Anda akan menghasilkan kode untuk memanggil fungsi kosong yang dikenal dalam rilis rilis, hal pertama dan terbesar yang perlu Anda lakukan untuk meningkatkan efisiensi adalah mendapatkan kompiler yang jauh lebih baik. Ini benar-benar optimasi sepele.
David Thornley
@Eenoku Tidak, seperti kata David, itu akan dihapus oleh kompiler yang harus Anda gunakan.
Jack Aidley
3
Ada perbedaan utama di sini: makro (tergantung pada bagaimana ditulisnya) tidak akan mengevaluasi ekspresi argumennya, sementara fungsinya selalu. Selain itu, perilaku yang tidak terdefinisi untuk meneruskan argumen non-sepele ke fungsi varargs (dan biasanya memicu peringatan kompiler).
Sebastian Redl
2

Jelas, pastikan Anda tidak menginjak pedoman kode yang diberikan oleh tim Anda. Pastikan tidak ada kode lain dalam sistem yang mencoba mencapai fungsi yang sama melalui kondisi umum jika.

James O
sumber