Rumah > Artikel > pembangunan bahagian belakang > Bagaimanakah Kami Boleh Melaksanakan Makro Rekursif dalam C?
Memahami Rekursi Makro untuk Argumen Makro
Dalam pengaturcaraan C, makro menawarkan alat yang berkuasa untuk manipulasi teks. Satu aspek yang menarik ialah keupayaan untuk menggunakan makro pada hujah makro lain. Walau bagaimanapun, ini memberikan cabaran teknikal, kerana makro rekursif biasanya tidak dibenarkan dalam bahasa.
Masalah: Makro Rekursif
Pertimbangkan senario yang ingin kami buat makro foreach, bernama PRINT_ALL, yang menggunakan makro tertentu, PRINT, pada senarai argumen. Contohnya:
int a = 1, b = 3, d = 0; PRINT_ALL(a,b,d);
Ini akan menggunakan makro CETAK pada setiap pembolehubah a, b dan d. Pendekatan naif mungkin menggunakan makro rekursif, seperti berikut:
#define FIRST_ARG(arg,...) arg #define AFTER_FIRST_ARG(arg,...) , ##__VA_ARGS__ #define PRINT(a) printf(#a": %d", a) #define PRINT_ALL PRINT(FIRST_ARG(__VA_ARGS__)); PRINT_ALL(AFTER_FIRST_ARG(__VA_ARGS__))
Walau bagaimanapun, pendekatan ini menimbulkan dua masalah: makro tidak boleh memanggil diri mereka secara rekursif dan ia tidak mempunyai syarat berhenti untuk menghentikan rekursi.
Penyelesaian Rekursif
Untuk mengatasinya halangan, penyelesaian yang bijak memanfaatkan teknik yang dikenali sebagai makro eval-rekursion. Idea utama adalah untuk mengeluarkan teks makro yang mensimulasikan panggilan makro tanpa menggunakan makro itu sendiri.
Pertimbangkan makro berikut:
#define MAP_OUT
Jika kita mempunyai makro berikut:
#define A(x) x B MAP_OUT (x) #define B(x) x A MAP_OUT (x)
Menilai makro A(blah) menghasilkan teks output:
blah B (blah)
Teks ini berfungsi sebagai pemegang tempat pengganti makro. Ia boleh dihantar semula ke dalam prapemproses untuk dikembangkan lagi, meneruskan proses penilaian makro.
Untuk memudahkan pengulangan ini, satu siri makro EVAL ditakrifkan:
#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 makro digunakan pelbagai peringkat penilaian, sekali gus menguatkan kesan makro yang digunakan.
Menghentikan Rekursi
Untuk mengawal rekursi, makro khas, MAP_END, ditakrifkan:
#define MAP_END(...)
Menilai makro ini tidak melakukan apa-apa, menamatkan rekursi dengan berkesan.
Cabaran seterusnya ialah untuk menentukan masa untuk menggunakan MAP_END dan bukannya meneruskan rekursi. Untuk mencapai matlamat ini, makro MAP_NEXT membandingkan item senarai dengan penanda akhir senarai khas. Jika ia sepadan, ia mengembalikan MAP_END; jika tidak, ia mengembalikan parameter seterusnya:
#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)
Dengan membina makro MAP_NEXT dengan teliti, kita boleh mengawal sama ada rekursi berterusan atau tamat.
Pelaksanaan Akhir
Menggabungkan blok binaan ini, kita boleh mencipta makro MAP yang berulang ke atas senarai dan menggunakan yang diberikan makro kepada setiap item:
#define MAP(f,...)EVAL(MAP1(f,__VA_ARGS__,(),0))
Makro ini berfungsi dengan meletakkan penanda senarai akhir di hujung senarai, bersama-sama dengan hujah tambahan untuk memastikan pematuhan ANSI. Ia kemudian melepasi senarai melalui berbilang panggilan makro EVAL dan mengembalikan hasilnya.
Teknik ini menyediakan penyelesaian kreatif kepada masalah penggunaan makro pada hujah makro. Ia membolehkan keupayaan manipulasi makro yang canggih, membenarkan pengaturcara memanjangkan fungsi prapemproses dalam cara yang baharu.
Atas ialah kandungan terperinci Bagaimanakah Kami Boleh Melaksanakan Makro Rekursif dalam C?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!