Membuat makro C dengan ## dan __LINE__ (penggabungan token dengan makro pemosisian)

107

Saya ingin membuat makro C yang membuat fungsi dengan nama berdasarkan nomor baris. Saya pikir saya bisa melakukan sesuatu seperti (fungsi sebenarnya akan memiliki pernyataan di dalam kurung):

#define UNIQUE static void Unique_##__LINE__(void) {}

Yang saya harap akan berkembang menjadi seperti:

static void Unique_23(void) {}

Itu tidak berhasil. Dengan penggabungan token, makro pemosisian diperlakukan secara harfiah, yang akhirnya meluas ke:

static void Unique___LINE__(void) {}

Apakah ini mungkin dilakukan?

(Ya, ada alasan nyata saya ingin melakukan ini tidak peduli betapa tidak berguna ini tampaknya).

DD.
sumber
Saya pikir Anda bisa mendapatkan ini untuk bekerja dengan ekspansi makro tidak langsung .
Ben Stiglitz
4
kemungkinan duplikat Bagaimana menggabungkan dua kali dengan praprosesor C dan memperluas makro seperti pada "arg ## _ ## MACRO"? Hal yang sama berlaku untuk makro selain __LINE__(meskipun itu adalah kasus penggunaan umum.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

176

Masalahnya adalah ketika Anda memiliki pengganti makro, preprocessor hanya akan memperluas makro secara rekursif jika baik operator merangkai #maupun operator penempelan token ##diterapkan padanya. Jadi, Anda harus menggunakan beberapa lapisan tambahan tipuan, Anda dapat menggunakan operator penempelan token dengan argumen yang diperluas secara rekursif:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}

Kemudian, __LINE__akan diperluas ke nomor baris selama perluasan UNIQUE(karena tidak terlibat dengan salah satu #atau ##), dan kemudian penempelan token terjadi selama perluasan TOKENPASTE.

Perlu juga dicatat bahwa ada juga __COUNTER__makro, yang meluas ke bilangan bulat baru setiap kali dievaluasi, jika Anda perlu memiliki beberapa contoh UNIQUEmakro pada baris yang sama. Catatan: __COUNTER__didukung oleh MS Visual Studio, GCC (sejak V4.3), dan Clang, tetapi tidak standar C.

Adam Rosenfield
sumber
3
Saya khawatir itu tidak berfungsi dengan GNU cpp. TOKENPASTE menggunakan LINE sebagai literal. TOKENPASTE (Unique_, LINE ) diperluas menjadi Unique___LINE__
DD.
3
@DD: Duh, sudah diperbaiki sekarang. Ini membutuhkan 2 lapisan tipuan, bukan 1.
Adam Rosenfield
The __COUNTER__makro tidak bekerja untuk saya di gcc; meskipun __LINE__salah satunya berfungsi seperti yang diiklankan.
Tyler
2
Sedikit informasi tambahan untuk siapa pun yang mencoba COUNTER , menurut msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx itu adalah makro khusus untuk Microsoft.
Elva
3
Ada penjelasan mengapa Anda membutuhkan 2 tingkat tipuan? Saya sudah mencobanya hanya dengan satu, tanpa # dan ##, dan itu tidak mengembangkannya di VS2017. Ternyata hal yang sama berlaku untuk GCC. Tetapi jika Anda menambahkan tipuan tingkat ke-2, maka itu akan meluas. Sihir?
Gabe Halsmer
-2

GCC tidak memerlukan "pembungkusan" (atau realisasi) kecuali hasilnya harus "dirangkai". Gcc memiliki fitur tetapi SEMUA dapat dilakukan dengan C versi 1 biasa (dan beberapa orang berpendapat Berkeley 4.3 C jauh lebih cepat sehingga perlu dipelajari cara menggunakannya).

** Dentang (llvm) TIDAK MELAKUKAN RUANG PUTIH DENGAN BENAR untuk perluasan makro - ia menambahkan spasi (yang tentunya menghancurkan hasil sebagai Pengenal C untuk pra-pemrosesan lebih lanjut) **, dentang tidak melakukan # atau * perluasan makro seperti yang diharapkan oleh C Preprocessor selama beberapa dekade. Contoh utama adalah kompilasi X11, makro "Concat3" rusak, hasilnya sekarang adalah MISNAMED C Identifier, yang tentu saja gagal dibangun. dan saya mulai merasa gagal membangun adalah profesi mereka.

Saya pikir jawabannya di sini adalah "C baru yang melanggar standar adalah C buruk", peretasan ini selalu memilih untuk (ruang nama clobber) mereka mengubah default tanpa alasan tetapi tidak benar-benar "meningkatkan C" (kecuali mereka sendiri mengatakan demikian: yang i say adalah alat yang dibuat untuk menjelaskan mengapa mereka lolos dengan semua kerusakan yang belum ada yang membuat mereka bertanggung jawab).


Bukan masalah bahwa pra-pemroses C sebelumnya tidak mendukung UNIq_ () __ karena mereka mendukung #pragma yang memungkinkan "peretasan merek kompilator dalam kode ditandai sebagai peretasan" dan juga berfungsi dengan baik TANPA memengaruhi standar: sama seperti mengubah defaultnya adalah kerusakan wonton yang tidak berguna, dan sama seperti mengubah fungsi yang dilakukan saat menggunakan nama yang sama (namespace clobbering) adalah ... malware menurut saya

tidak ada yang dibenci
sumber