Saya mencoba untuk menulis sebuah program di mana nama-nama beberapa fungsi tergantung pada nilai variabel makro tertentu dengan makro seperti ini:
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
int NAME(some_function)(int a);
Sayangnya, makro NAME()
mengubahnya menjadi
int some_function_VARIABLE(int a);
daripada
int some_function_3(int a);
jadi ini jelas cara yang salah untuk melakukannya. Untungnya, jumlah nilai yang mungkin berbeda untuk VARIABEL kecil sehingga saya bisa melakukan #if VARIABLE == n
dan membuat daftar semua kasus secara terpisah, tetapi saya bertanya-tanya apakah ada cara cerdas untuk melakukannya.
#define A 0 \n #define M a ## A
: memiliki dua##
bukanlah kuncinya.Jawaban:
Preprosesor C Standar
Dua tingkat tipuan
Dalam komentar untuk jawaban lain, Cade Roux bertanya mengapa ini membutuhkan dua tingkat tipuan. Jawaban kurang ajar adalah karena begitulah standar mengharuskannya bekerja; Anda cenderung menemukan bahwa Anda memerlukan trik yang setara dengan operator merangkai juga.
Bagian 6.10.3 dari standar C99 mencakup 'penggantian makro', dan 6.10.3.1 mencakup 'substitusi argumen'.
Dalam doa
NAME(mine)
, argumennya adalah 'milikku'; itu sepenuhnya diperluas menjadi 'milikku'; itu kemudian diganti ke dalam string pengganti:Sekarang EVALUATOR makro ditemukan, dan argumen diisolasi sebagai 'milikku' dan 'VARIABEL'; yang terakhir ini kemudian sepenuhnya diperluas ke '3', dan diganti menjadi string pengganti:
Operasi ini dicakup oleh aturan lain (6.10.3.3 'Operator ##'):
Jadi, daftar pengganti berisi
x
diikuti oleh##
dan juga##
diikuti olehy
; jadi kita punya:dan menghilangkan
##
token dan menggabungkan token di kedua sisi menggabungkan 'milikku' dengan '_' dan '3' untuk menghasilkan:Ini adalah hasil yang diinginkan.
Jika kita melihat pertanyaan aslinya, kode itu (disesuaikan untuk menggunakan 'milikku' alih-alih 'fungsi_beberapa'):
Argumen untuk NAME jelas 'milikku' dan itu diperluas sepenuhnya.
Mengikuti aturan 6.10.3.3, kami menemukan:
yang, ketika
##
operator dieliminasi, memetakan ke:persis seperti yang dilaporkan dalam pertanyaan.
Preprosesor C Tradisional
Robert Rüger bertanya :
Mungkin, dan mungkin tidak - itu tergantung pada preprosesor. Salah satu kelebihan dari preprocessor standar adalah memiliki fasilitas ini yang bekerja dengan andal, sedangkan terdapat implementasi yang berbeda untuk preprosesor pra-standar. Salah satu persyaratan adalah bahwa ketika preprocessor mengganti komentar, ia tidak menghasilkan spasi seperti yang harus dilakukan preprocessor ANSI. GCC (6.3.0) C Preprocessor memenuhi persyaratan ini; preprosesor Dentang dari XCode 8.2.1 tidak.
Ketika berhasil, ini berhasil (
x-paste.c
):Perhatikan bahwa tidak ada spasi di antara
fun,
danVARIABLE
- itu penting karena jika ada, itu disalin ke output, dan Anda berakhir denganmine_ 3
namanya, yang tentu saja tidak valid secara sintaksis. (Sekarang, bisakah saya mengembalikan rambut saya?)Dengan GCC 6.3.0 (berjalan
cpp -traditional x-paste.c
), saya mendapatkan:Dengan Dentang dari XCode 8.2.1, saya mendapatkan:
Ruang-ruang itu merusak segalanya. Saya perhatikan bahwa kedua preprosesor sudah benar; preprosesor pra-standar yang berbeda memamerkan kedua perilaku tersebut, yang membuat token menempelkan proses yang sangat menjengkelkan dan tidak dapat diandalkan ketika mencoba memasukkan kode. Standar dengan
##
notasi secara radikal menyederhanakan itu.Mungkin ada cara lain untuk melakukan ini. Namun, ini tidak berhasil:
GCC menghasilkan:
Tutup, tapi tidak ada dadu. YMMV, tentu saja, tergantung pada preprosesor pra-standar yang Anda gunakan. Terus terang, jika Anda terjebak dengan preprocessor yang tidak bekerja sama, mungkin akan lebih mudah untuk mengatur untuk menggunakan preprocessor C standar di tempat yang pra-standar (biasanya ada cara untuk mengkonfigurasi kompilator dengan tepat) daripada menghabiskan banyak waktu mencoba mencari cara untuk melakukan pekerjaan itu.
sumber
cpp -traditional
. Perhatikan bahwa tidak ada jawaban pasti - itu tergantung pada preprosesor yang Anda dapatkan.Jujur, Anda tidak ingin tahu mengapa ini berhasil. Jika Anda tahu mengapa itu berhasil, Anda akan menjadi pria di tempat kerja yang mengetahui hal semacam ini, dan semua orang akan datang menanyakan pertanyaan kepada Anda. =)
Sunting: jika Anda benar-benar ingin tahu mengapa itu bekerja, saya akan dengan senang hati mengirim penjelasan, dengan asumsi tidak ada yang mengalahkan saya untuk itu.
sumber