Tren pengembangan C ++ modern menyarankan kemungkinan maksimum penolakan makro dalam kode. Tetapi kadang-kadang tanpa makro, dan dalam manifestasi mereka yang jelek, seseorang tidak dapat melakukannya, karena tanpa mereka itu bahkan lebih buruk. Tentang ini dan ceritanya.
Seperti yang Anda ketahui, langkah pertama dalam kompilasi C dan C ++ adalah preprocessor, yang menggantikan macro dan arahan preprocessor dengan teks biasa.
Ini memungkinkan kita melakukan hal-hal aneh, misalnya, seperti:
Setelah preprosesor berfungsi, kesalahpahaman ini akan berubah menjadi kode yang benar:
std::string str = "look, I'm a string!" ;
Tentu saja, tajuk yang mengerikan ini tidak dapat dimasukkan di tempat lain. Dan ya, karena fakta bahwa kami akan menambahkan header ini beberapa kali ke file yang sama - tidak ada #pragma sekali atau menyertakan penjaga.
Sebenarnya, mari kita tulis contoh yang lebih kompleks yang akan melakukan hal-hal berbeda dengan bantuan makro dan pada saat yang sama mempertahankan terhadap #include acak:
Ini masih jelek, tetapi pesona tertentu sudah muncul: ketika Anda menambahkan elemen baru ke kelas enum, itu akan secara otomatis ditambahkan ke pernyataan output yang kelebihan beban.
Di sini Anda dapat memformalkan bidang penerapan metode ini: kebutuhan untuk pembuatan kode di tempat yang berbeda berdasarkan pada satu sumber.
Dan sekarang kisah sedih X-Macro dan Windows. Ada sistem seperti Penghitung Kinerja Windows, yang memungkinkan Anda mengirim penghitung tertentu ke sistem operasi sehingga aplikasi lain dapat mengambilnya. Misalnya, Zabbix dapat dikonfigurasi untuk mengumpulkan dan memantau Penghitung Kinerja apa pun. Ini cukup nyaman, dan Anda tidak perlu menemukan kembali roda dengan pengembalian / kueri data.
Saya dengan tulus berpikir bahwa menambahkan penghitung baru terlihat seperti HANDLE counter = AddCounter ("name"). Ah, betapa salahnya aku.
Pertama, Anda perlu menulis manifes XML khusus (
contoh ), atau membuatnya menggunakan program ecmangen.exe dari Windows SDK, tetapi karena
alasan tertentu ecmangen
ini telah dihapus dari versi baru Windows 10 SDK. Selanjutnya, Anda perlu membuat kode dan file .rc menggunakan utilitas
ctrpp berdasarkan manifes XML kami.
Menambahkan penghitung baru ke sistem itu sendiri dilakukan hanya dengan utilitas
lodctr dengan manifes XML kami dalam argumen.
Apa itu file .rc?Ini adalah penemuan Microsoft, tidak terkait dengan standar C ++. Menggunakan file-file ini, Anda dapat menanamkan sumber daya di exe \ dll, seperti string \ icons \ gambar, dll, dan kemudian mengambilnya menggunakan Windows API khusus.
Perfcounters menggunakan file .rc ini untuk melokalkan nama penghitung, dan tidak terlalu jelas mengapa nama ini harus dilokalkan.
Ringkas hal di atas: untuk menambahkan 1 penghitung yang Anda butuhkan:
- Ubah manifes XML
- Hasilkan file proyek .c dan .rc baru berdasarkan manifes
- Tulis fungsi baru yang akan menambah penghitung baru
- Tulis fungsi baru yang akan mengambil nilai penghitung
Total: 4-5 file yang dimodifikasi dalam diff-e demi counter tunggal dan terus-menerus menderita bekerja dengan manifes XML, yang merupakan sumber informasi dalam kode plus. Inilah yang ditawarkan Microsoft kepada kami.
Sebenarnya, solusi yang diciptakan terlihat menakutkan, tetapi menambahkan penghitung baru dilakukan tepat 1 baris dalam satu file. Lebih jauh, semuanya dihasilkan secara otomatis menggunakan makro dan, sayangnya, skrip pra-bangun, karena manifes XML masih diperlukan, meskipun sekarang bukan yang utama.
Perfcounters_ctr.h kami terlihat hampir identik dengan contoh di atas:
#ifndef NV_PERFCOUNTER #error "You cannot do this!" #endif ... NV_PERFCOUNTER(copied_bytes) NV_PERFCOUNTER(copied_files) ... #undef NV_PERFCOUNTER
Seperti yang saya tulis sebelumnya, menambahkan penghitung dilakukan dengan memuat manifes XML menggunakan lodctr.exe. Dari program kami, kami hanya dapat menginisialisasi dan memodifikasinya.
Fragmen inisialisasi yang menarik bagi kami dalam tampilan pembuka yang dibuat seperti ini:
#define COPIED_BYTES 0
Total: kita perlu korespondensi dari bentuk "nama lawan - indeks yang meningkat", dan pada tahap kompilasi, kita perlu mengetahui jumlah penghitung dan mengumpulkan larik inisialisasi dari indeks penghitung. Di sinilah X-makro datang untuk menyelamatkan.
Mencocokkan nama lawan dengan kenaikan indeksnya cukup sederhana.
Kode di bawah ini akan berubah menjadi kelas enum, yang indeks internalnya mulai dari 0, dan bertambah satu. Menambahkan elemen terakhir dengan tangan kami, kami segera mengetahui berapa total penghitung yang kami miliki:
enum class counter_enum : int { #define NV_PERFCOUNTER(ctr) ctr, #include "perfcounters_ctr.h" total_counters };
Dan selanjutnya, berdasarkan enum kami, kami perlu menginisialisasi penghitung:
static constexpr int counter_count = static_cast<int>(counter_enum::total_counters); const PERF_COUNTERSET_INFO counterset_info{ ... counter_count, ... }; struct { PERF_COUNTERSET_INFO set; PERF_COUNTER_INFO counters[counter_count]; } counterset { counterset_info, {
Hasilnya adalah bahwa inisialisasi penghitung baru sekarang mengambil 1 baris dan tidak memerlukan perubahan tambahan dalam file lain (sebelumnya, setiap regenerasi mengubah 3 buah kode hanya dalam inisialisasi).
Dan mari kita tambahkan API yang nyaman untuk menambah penghitung. Sesuatu dalam roh:
#define NV_PERFCOUNTER(ctr) \ inline void ctr##_tick(size_t diff = 1) { } #include "perfcounters_ctr.h" #define NV_PERFCOUNTER(ctr) \ inline size_t ctr##_get() { } #include "perfcounter_ctr.h"
Preprocessor akan menghasilkan getter / setter cantik untuk kita, yang dapat segera kita gunakan dalam kode, misalnya:
inline void copied_bytes_tick(size_t diff = 1); inline size_t copied_bytes_get();
Tapi kami masih memiliki 2 hal yang menyedihkan: manifes XML dan file .rc (sayangnya, itu perlu).
Kami membuatnya cukup sederhana - skrip pra-bangun yang membaca file asli dengan macro mendefinisikan counter, mem-parsing apa yang ada di antara "NV_COUNTER (" dan ")", dan berdasarkan ini ia menghasilkan kedua file yang ada di .gitignore sehingga jangan membuang sampah sembarangan.
Itu adalah : Perangkat lunak khusus berdasarkan kode pengkodean manifes yang dihasilkan XML. Banyak perubahan dalam proyek untuk setiap penambahan / penghapusan penghitung.
Sekarang: Skrip preprocessor dan prebuild menghasilkan semua penghitung, manifes XML dan file .rc. Tepat satu baris di diff-e untuk menambah / menghapus penghitung. Terima kasih kepada preprocessor yang membantu menyelesaikan masalah ini, menunjukkan dalam kasus khusus ini lebih baik daripada merugikan.