Rumah > Artikel > pembangunan bahagian belakang > Bagaimana untuk Mentakrifkan Makro Yang Beroperasi pada Argumen yang Diserahkan kepada Makro Lain dalam C?
Bagaimanakah seseorang boleh mentakrifkan makro yang beroperasi pada hujah yang dihantar kepada makro lain? Khususnya, pertimbangkan perkara berikut:
#define PRINT(a) printf(#a": %d", a) #define PRINT_ALL(...) ? ? ? THE PROBLEM ? ? ?
Penggunaan yang Diingini:
int a = 1, b = 3, d = 0; PRINT_ALL(a,b,d);
Makro rekursif boleh dilakukan dalam C menggunakan penyelesaian yang tidak konvensional. Matlamatnya ialah untuk mencipta makro MAP yang menyerupai:
#define PRINT(a) printf(#a": %d", a) MAP(PRINT, a, b, c) /* Apply PRINT to a, b, and c */
Kami mulakan dengan mencipta cara untuk mengeluarkan sesuatu yang menyerupai panggilan makro yang tidak dinilai:
#define MAP_OUT
Mempertimbangkan makro berikut:
#define A(x) x B MAP_OUT (x) #define B(x) x A MAP_OUT (x)
Menilai A(blah) menghasilkan teks:
blah B (blah)
Prapemproses tidak mengesan rekursi di sini, kerana panggilan B (bla) hanyalah teks pada peringkat ini. Memasukkan semula teks ini ke dalam prapemproses mengembangkannya:
blah blah A (blah)
Menilai output ini sekali lagi mengembangkan makro A (bla), melengkapkan rekursi. Proses ini berterusan sehingga output dimasukkan semula ke dalam prapemproses sekali lagi.
Untuk memudahkan penilaian berbilang, kami menggunakan makro EVAL untuk meneruskan argumen ke bawah rantaian panggilan makro:
#define EVAL0(...) __VA_ARGS__ #define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__))) #define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__))) #define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__))) #define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__))) #define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
Setiap tahap menguatkan usaha pendahulunya, akhirnya menilai input 365 kali. Contohnya, EVAL(A(bla)) menghasilkan 365 salinan blah diikuti oleh B yang tidak dinilai (bla). Persediaan ini menyediakan asas untuk rekursi dalam had kedalaman tindanan tertentu.
Halangan seterusnya ialah menentukan masa untuk menamatkan rekursi.
Kami mentakrifkan makro MAP_END sebagai penanda senarai akhir:
#define MAP_END(...)
Menilai ini makro tidak melakukan apa-apa, menandakan berakhirnya rekursi.
Untuk memilih antara dua makro, kami menggunakan MAP_NEXT, yang membandingkan item senarai dengan penanda akhir senarai khas ():
#define MAP_GET_END() 0, MAP_END #define MAP_NEXT0(item, next, ...) next MAP_OUT #define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0) #define MAP_NEXT(item, next) MAP_NEXT1 (MAP_GET_END item, next)
MAP_NEXT menentukan sama ada untuk meneruskan atau berhenti berdasarkan item senarai dan item seterusnya. Ia mengembalikan MAP_END jika ia sepadan atau parameter seterusnya sebaliknya.
Dengan komponen ini, kami boleh membina versi A dan B yang boleh digunakan:
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__) #define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
Makro ini menggunakan operasi f pada item senarai semasa x dan kemudian memeriksa item senarai seterusnya, mengintip, untuk menentukan sama ada untuk meneruskan atau tidak.
Akhir sekali, kami mengumpulkan segala-galanya dalam makro MAP peringkat atas:
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
Makro ini menambah penanda () pada penghujung senarai dan melepasi keseluruhannya melalui EVAL , mengembalikan hasilnya.
Kod ini tersedia sebagai pustaka di GitHub untuk kemudahan anda.
Atas ialah kandungan terperinci Bagaimana untuk Mentakrifkan Makro Yang Beroperasi pada Argumen yang Diserahkan kepada Makro Lain dalam C?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!