Penggabungan string makro C / C ++

121
#define STR1      "s"
#define STR2      "1"
#define STR3      STR1 ## STR2

Apakah mungkin untuk menggabungkan STR3 == "s1"? Anda dapat melakukan ini dengan meneruskan args ke fungsi Makro lainnya. Tetapi apakah ada cara langsung?

tvr
sumber
Bukankah seharusnya
#tentukan
Seharusnya tidak demikian karena itu mendefinisikan STR3 sebagai token praproses STR1STR2. Dan meneruskan arg ke fungsi makro lain tidak membantu, karena string literal tidak dapat ditempelkan bersama - "s" "1" bukanlah token yang valid.
Jim Balter

Jawaban:

157

Jika keduanya adalah string, Anda bisa melakukan:

#define STR3 STR1 STR2

Praprosesor secara otomatis menggabungkan string yang berdekatan.

EDIT:

Seperti disebutkan di bawah ini, bukan preprocessor tetapi kompiler yang melakukan penggabungan.

Sean
sumber
17
Secara teknis penggabungan string dilakukan di tingkat bahasa.
Martin York
47
Praprosesor tidak melakukan hal seperti itu. Ini adalah bahasa C yang memperlakukan literal string yang berdekatan seolah-olah mereka adalah literal string tunggal.
Jim Balter
7
Ini lebih dari sekedar teknis - Anda tidak dapat menggabungkan L"a"dan "b"mendapatkan L"ab", tetapi Anda dapat menggabungkan L"a"dan L"b"mendapatkan L"ab".
MSalters
115

Anda tidak memerlukan solusi semacam itu untuk string literal, karena mereka digabungkan pada tingkat bahasa, dan itu tidak akan berfungsi karena "s" "1" bukan token preprocessor yang valid.

[Sunting: Menanggapi komentar "Hanya untuk dicatat" yang salah di bawah ini yang sayangnya menerima beberapa suara positif, saya akan mengulangi pernyataan di atas dan mengamati bahwa fragmen program

#define PPCAT_NX(A, B) A ## B
PPCAT_NX("s", "1")

menghasilkan pesan kesalahan ini dari fase pra-pemrosesan gcc: kesalahan: menempelkan "" s "" dan "" 1 "" tidak memberikan token pra-pemrosesan yang valid

]

Namun, untuk penempelan token umum, coba ini:

/*
 * Concatenate preprocessor tokens A and B without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define PPCAT_NX(A, B) A ## B

/*
 * Concatenate preprocessor tokens A and B after macro-expanding them.
 */
#define PPCAT(A, B) PPCAT_NX(A, B)

Kemudian, misalnya, keduanya PPCAT_NX(s, 1)dan PPCAT(s, 1)menghasilkan pengenal s1, kecuali jika sdidefinisikan sebagai makro, dalam hal ini PPCAT(s, 1)menghasilkan <macro value of s>1.

Melanjutkan tema adalah makro ini:

/*
 * Turn A into a string literal without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define STRINGIZE_NX(A) #A

/*
 * Turn A into a string literal after macro-expanding it.
 */
#define STRINGIZE(A) STRINGIZE_NX(A)

Kemudian,

#define T1 s
#define T2 1
STRINGIZE(PPCAT(T1, T2)) // produces "s1"

Sebaliknya,

STRINGIZE(PPCAT_NX(T1, T2)) // produces "T1T2"
STRINGIZE_NX(PPCAT_NX(T1, T2)) // produces "PPCAT_NX(T1, T2)"

#define T1T2 visit the zoo
STRINGIZE(PPCAT_NX(T1, T2)) // produces "visit the zoo"
STRINGIZE_NX(PPCAT(T1, T2)) // produces "PPCAT(T1, T2)"
Jim Balter
sumber
8
Sekadar catatan, "s""1"valid dalam C (dan C ++). Mereka adalah dua token (string literal) yang akan disatukan oleh kompiler dan ancaman sebagai satu token.
Shahbaz
4
Anda salah paham baik komentar saya maupun bahasa C. Saya berkata "s""1" isn't a valid token- itu benar; itu, seperti yang Anda katakan, dua token. Tetapi menyatukannya dengan ## akan membuatnya menjadi token praproses tunggal , bukan dua token, sehingga compiler tidak akan melakukan penggabungan, melainkan lexer akan menolaknya (bahasa ini memerlukan diagnostik).
Jim Balter
8
@ mr5 Bacalah komentar dengan cermat. Nama makro yang diteruskan sebagai argumen makro tidak diperluas sebelum diteruskan. Namun, mereka diperluas dalam tubuh makro. Jadi jika A didefinisikan sebagai FRED, STRINGIZE_NX (A) diperluas menjadi "A" tetapi STRINGIZE (A) meluas menjadi STRINGIZE_NX (FRED) yang meluas menjadi "FRED".
Jim Balter
1
@bharath string yang dihasilkan adalah "PPCAT (T1, T2)" - seperti yang diharapkan dan diinginkan. dan bukan "s1" yang diharapkan - tidak diharapkan sama sekali. Mengapa kita membutuhkan tipu muslihat / bersarang ekstra? - Baca komentar kode, dan komentar saya di atas dengan 6 suara positif. Hanya badan makro yang diperluas; di luar badan makro, argumen makro di antara tanda kurung tidak diperluas sebelum diteruskan ke makro. Jadi STRINGIZE_NX(whatever occurs here)berkembang menjadi "apa pun yang terjadi di sini", terlepas dari definisi makro apa pun untuk apa pun, yang terjadi, atau di sini.
Jim Balter
1
@bharath Tentu saja tidak mencetak "Nama A" - A adalah nama parameter, bukan argumen ke makro, yaitu ALEX. Anda mengklaim if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- itu salah, dan tidak seperti ujian Anda. Anda berusaha keras untuk tidak memahami atau melakukan ini dengan benar, dan saya tidak akan menanggapi Anda lebih jauh.
Jim Balter
24

Petunjuk: STRINGIZEMakro di atas keren, tetapi jika Anda membuat kesalahan dan argumennya bukan makro - Anda memiliki kesalahan ketik pada namanya, atau lupa #includepada file header - maka kompilator akan dengan senang hati memasukkan nama makro yang diklaim ke dalam string tanpa kesalahan.

Jika Anda bermaksud agar argumen ke STRINGIZEselalu makro dengan nilai C normal, maka

#define STRINGIZE(A) ((A),STRINGIZE_NX(A))

akan mengembangkannya sekali dan memeriksa validitasnya, membuangnya, lalu mengembangkannya lagi menjadi string.

Butuh beberapa saat bagi saya untuk mencari tahu mengapa STRINGIZE(ENOENT)berakhir sebagai "ENOENT"alih-alih "2"... Saya tidak memasukkan errno.h.

Jordan Brown
sumber
2
Pengamatan penting, dan +1 untuk penggunaan ,operator yang tepat. :)
Jesse Chisholm
2
Tidak ada alasan khusus mengapa konten string harus berupa ekspresi C yang valid. Jika Anda ingin melakukannya, saya sarankan untuk memberinya nama yang berbeda, seperti STRINGIZE_EXPR.
Jim Balter
Trik itu mungkin berhasil dalam isolasi. Tapi itu mencegah kompilator melihat urutan string yang akan digabungkannya. (menghasilkan urutan seperti, ((1),"1") "." ((2),"2")bukan hanya "1" "." "2")
automorfik
Hanya untuk memperjelas apa yang dikatakan automorfik: dengan STRINGIZEdefinisi aslinya , "The value of ENOENT is " STRINGIZE(ENOENT)berfungsi, sedangkan "The value of ENOENT is" STRINGIZE_EXPR(X)menghasilkan kesalahan.
Jim Balter