Saya harus melakukan sesuatu seperti ini di C. Ini berfungsi hanya jika saya menggunakan char, tetapi saya membutuhkan string. Bagaimana saya bisa melakukan ini?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
c
conditional
c-preprocessor
frx08
sumber
sumber
Jawaban:
Saya tidak berpikir ada cara untuk melakukan perbandingan string panjang variabel sepenuhnya dalam arahan preprocessor. Anda mungkin bisa melakukan hal berikut ini:
#define USER_JACK 1 #define USER_QUEEN 2 #define USER USER_JACK #if USER == USER_JACK #define USER_VS USER_QUEEN #elif USER == USER_QUEEN #define USER_VS USER_JACK #endif
Atau Anda dapat mengubah sedikit kode dan menggunakan kode C.
sumber
#define USER_VS (3 - USER)
dalam kasus khusus ini. :)[PEMBARUAN: 2018.05.03]
PERHATIAN : Tidak semua kompiler mengimplementasikan spesifikasi C ++ 11 dengan cara yang sama. Kode di bawah ini berfungsi di kompiler yang saya uji, sementara banyak pemberi komentar menggunakan kompilator yang berbeda.
Mengutip dari jawaban Shafik Yaghmour pada: Menghitung panjang string C pada waktu kompilasi. Apakah ini benar-benar sebuah constexpr?
Kata itu
can
membuat semua perbedaan di dunia.Jadi, YMMV pada jawaban ini (atau apapun) yang melibatkan
constexpr
, tergantung pada interpretasi penulis kompilator terhadap spesifikasi.[DIPERBARUI 2016.01.31]
Karena beberapa tidak menyukai jawaban saya sebelumnya karena menghindari seluruh
compile time string compare
aspek OP dengan mencapai tujuan tanpa perlu membandingkan string, berikut adalah jawaban yang lebih rinci.Tidak boleh! Tidak di C98 atau C99. Bahkan tidak di C11. Tidak ada manipulasi MAKRO yang akan mengubah ini.
Definisi yang
const-expression
digunakan di#if
tidak mengizinkan string.Itu memungkinkan karakter, jadi jika Anda membatasi diri Anda pada karakter Anda dapat menggunakan ini:
#define JACK 'J' #define QUEEN 'Q' #define CHOICE JACK // or QUEEN, your choice #if 'J' == CHOICE #define USER "jack" #define USER_VS "queen" #elif 'Q' == CHOICE #define USER "queen" #define USER_VS "jack" #else #define USER "anonymous1" #define USER_VS "anonymous2" #endif #pragma message "USER IS " USER #pragma message "USER_VS IS " USER_VS
Kamu bisa! Di C ++ 11. Jika Anda mendefinisikan fungsi pembantu waktu kompilasi untuk perbandingan.
// compares two strings in compile time constant fashion constexpr int c_strcmp( char const* lhs, char const* rhs ) { return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0 : (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0]) : c_strcmp( lhs+1, rhs+1 ); } // some compilers may require ((int)lhs[0] - (int)rhs[0]) #define JACK "jack" #define QUEEN "queen" #define USER JACK // or QUEEN, your choice #if 0 == c_strcmp( USER, JACK ) #define USER_VS QUEEN #elif 0 == c_strcmp( USER, QUEEN ) #define USER_VS JACK #else #define USER_VS "unknown" #endif #pragma message "USER IS " USER #pragma message "USER_VS IS " USER_VS
Jadi, pada akhirnya, Anda harus mengubah cara Anda mencapai tujuan Anda dalam memilih nilai string akhir untuk
USER
danUSER_VS
.Anda tidak dapat melakukan kompilasi string waktu membandingkan di C99, tetapi Anda dapat melakukan waktu kompilasi untuk memilih string.
Jika Anda benar-benar harus melakukan perbandingan sengatan waktu kompilasi, maka Anda perlu mengubah ke C ++ 11 atau varian yang lebih baru yang memungkinkan fitur itu.
[ASLI JAWABAN IKUTI]
Mencoba:
#define jack_VS queen #define queen_VS jack #define USER jack // jack or queen, your choice #define USER_VS USER##_VS // jack_VS or queen_VS // stringify usage: S(USER) or S(USER_VS) when you need the string form. #define S(U) S_(U) #define S_(U) #U
PEMBARUAN: Penempelan token ANSI terkadang kurang jelas. ;-D
Menempatkan satu
#
sebelum makro menyebabkannya berubah menjadi string nilainya, bukan nilai kosongnya.Menempatkan tanda ganda di
##
antara dua token menyebabkan keduanya digabungkan menjadi satu token.Jadi, makro
USER_VS
memiliki perluasanjack_VS
atauqueen_VS
, bergantung pada cara Anda mengaturUSER
.The stringify makro
S(...)
menggunakan tipuan makro sehingga nilai makro bernama akan dikonversi menjadi string. alih-alih nama makro.Jadi
USER##_VS
menjadijack_VS
(atauqueen_VS
), tergantung bagaimana Anda mengaturUSER
.Kemudian, ketika makro stringify digunakan sebagai
S(USER_VS)
nilaiUSER_VS
(jack_VS
dalam contoh ini) diteruskan ke langkah tipuanS_(jack_VS)
yang mengubah nilainya (queen
) menjadi string"queen"
.Jika Anda set
USER
kequeen
maka hasil akhirnya adalah string"jack"
.Untuk penggabungan token, lihat: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
Untuk konversi string token, lihat: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
[DIPERBARUI 2015.02.15 untuk memperbaiki kesalahan ketik.]
sumber
#if 0 == c_strcmp( USER, JACK )
keconstexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
. Contoh Anda hanya berfungsi karena USER adalah JACK. Jika PENGGUNA adalah RATU, maka akanUSER IS QUEEN
USER_VS IS QUEEN
constexpr
) dari arahan preprocessor.Berikut ini bekerja untuk saya dengan dentang. Mengizinkan apa yang muncul sebagai perbandingan nilai makro simbolis. #error xxx hanya untuk melihat apa yang sebenarnya dilakukan oleh kompilator. Mengganti definisi cat dengan #define cat (a, b) a ## b merusak sesuatu.
#define cat(a,...) cat_impl(a, __VA_ARGS__) #define cat_impl(a,...) a ## __VA_ARGS__ #define xUSER_jack 0 #define xUSER_queen 1 #define USER_VAL cat(xUSER_,USER) #define USER jack // jack or queen #if USER_VAL==xUSER_jack #error USER=jack #define USER_VS "queen" #elif USER_VAL==xUSER_queen #error USER=queen #define USER_VS "jack" #endif
sumber
Gunakan nilai numerik sebagai ganti string.
Terakhir untuk mengubah konstanta JACK atau QUEEN menjadi string, gunakan operator stringize (dan / atau tokenisasi).
sumber
Seperti yang telah disebutkan di atas, preprocessor ISO-C11 tidak mendukung perbandingan string. Namun, masalah penetapan makro dengan "nilai berlawanan" dapat diselesaikan dengan "penempelan token" dan "akses tabel". Solusi makro concatenate / stringify Jesse yang sederhana gagal dengan gcc 5.4.0 karena stringisasi dilakukan sebelum evaluasi penggabungan (sesuai dengan ISO C11). Namun, itu bisa diperbaiki:
#define P_(user) user ## _VS #define VS(user) P_ (user) #define S(U) S_(U) #define S_(U) #U #define jack_VS queen #define queen_VS jack S (VS (jack)) S (jack) S (VS (queen)) S (queen) #define USER jack // jack or queen, your choice #define USER_VS USER##_VS // jack_VS or queen_VS S (USER) S (USER_VS)
Baris pertama (makro
P_()
) menambahkan satu tipuan untuk membiarkan baris berikutnya (makroVS()
) menyelesaikan penggabungan sebelum stringisasi (lihat Mengapa saya memerlukan lapisan ganda dari tipuan untuk makro? ). Makro stringisasi (S()
danS_()
) berasal dari Jesse.Tabel (makro
jack_VS
danqueen_VS
) yang jauh lebih mudah dipelihara daripada konstruksi OP jika-maka-lain berasal dari Jesse.Terakhir, blok empat baris berikutnya memanggil makro gaya fungsi. Blok empat baris terakhir adalah dari jawaban Jesse.
Menyimpan kode
foo.c
dan menjalankan preprocessorgcc -nostdinc -E foo.c
menghasilkan:# 1 "foo.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "foo.c" # 9 "foo.c" "queen" "jack" "jack" "queen" "jack" "USER_VS"
Outputnya seperti yang diharapkan. Baris terakhir menunjukkan bahwa
USER_VS
makro tidak diperluas sebelum stringisasi.sumber
#if (S(USER)=="jack")
- Saya mendapatkan kesalahan preprocessor saat menggunakan"
-error: invalid token at start of a preprocessor expression
.Jika string Anda adalah konstanta waktu kompilasi (seperti dalam kasus Anda), Anda dapat menggunakan trik berikut:
#define USER_JACK strcmp(USER, "jack") #define USER_QUEEN strcmp(USER, "queen") #if $USER_JACK == 0 #define USER_VS USER_QUEEN #elif USER_QUEEN == 0 #define USER_VS USER_JACK #endif
Kompilator dapat mengetahui hasil strcmp sebelumnya dan akan mengganti strcmp dengan hasilnya, sehingga memberi Anda #define yang dapat dibandingkan dengan arahan preprocessor. Saya tidak tahu apakah ada perbedaan antara kompiler / ketergantungan pada opsi kompilator, tetapi ini berhasil untuk saya di GCC 4.7.2.
EDIT: setelah penyelidikan lebih lanjut, sepertinya ini adalah ekstensi toolchain, bukan ekstensi GCC, jadi pertimbangkan itu ...
sumber
$
semacam ekstensi pra-prosesor?Jawaban dari Patrick dan oleh Jesse Chisholm membuat saya melakukan hal berikut:
#define QUEEN 'Q' #define JACK 'J' #define CHECK_QUEEN(s) (s==QUEEN) #define CHECK_JACK(s) (s==JACK) #define USER 'Q' [... later on in code ...] #if CHECK_QUEEN(USER) compile_queen_func(); #elif CHECK_JACK(USER) compile_jack_func(); #elif #error "unknown user" #endif
Dari pada
#define USER 'Q'
#define USER QUEEN
juga harus berfungsi tetapi tidak diujijuga berfungsi dan mungkin lebih mudah ditangani.EDIT: Menurut komentar @ Jean-François Fabre, saya menyesuaikan jawaban saya.
sumber
(s==QUEEN?1:0)
dengan(s==QUEEN)
Anda tidak perlu terner, hasilnya sudah boolean#define USER_IS(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9)\ ch0==c0 && ch1==c1 && ch2==c2 && ch3==c3 && ch4==c4 && ch5==c5 && ch6==c6 && ch7==c7 ; #define ch0 'j' #define ch1 'a' #define ch2 'c' #define ch3 'k' #if USER_IS('j','a','c','k',0,0,0,0) #define USER_VS "queen" #elif USER_IS('q','u','e','e','n',0,0,0) #define USER_VS "jack" #endif
itu pada dasarnya array karakter statis dengan panjang tetap diinisialisasi secara manual daripada panjang variabel array karakter statis diinisialisasi secara otomatis selalu diakhiri dengan penghentian char null
sumber
Anda tidak dapat melakukannya jika USER didefinisikan sebagai string yang dikutip.
Tetapi Anda dapat melakukannya jika PENGGUNA hanyalah JACK atau QUEEN atau Joker atau apapun.
Ada dua trik untuk digunakan:
#define JACK
melakukan sesuatuJadi mari kita mulai dengan:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
Sekarang, jika saya menulis
JACK_QUEEN_OTHER(USER)
, dan USER adalah JACK, preprocessor mengubahnya menjadiEXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
Langkah kedua adalah penggabungan:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
Sekarang
JACK_QUEEN_OTHER(USER)
menjadiEXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
Ini memberi kesempatan untuk menambahkan sejumlah koma sesuai dengan apakah string cocok atau tidak:
#define ReSeRvEd_JACK x,x,x #define ReSeRvEd_QUEEN x,x
Jika USER adalah JACK,
JACK_QUEEN_OTHER(USER)
menjadiEXPANSION2(x,x,x, 1, 2, 3)
Jika USER adalah QUEEN,
JACK_QUEEN_OTHER(USER)
menjadiEXPANSION2(x,x, 1, 2, 3)
Jika USER lain,
JACK_QUEEN_OTHER(USER)
menjadiEXPANSION2(ReSeRvEd_other, 1, 2, 3)
Pada titik ini, sesuatu yang kritis telah terjadi: argumen keempat pada makro EXPANSION2 adalah 1, 2, atau 3, bergantung pada apakah argumen asli yang diberikan adalah jack, queen, atau lainnya. Jadi yang harus kita lakukan adalah memilihnya. Untuk alasan yang bertele-tele, kita membutuhkan dua makro untuk langkah terakhir; mereka akan menjadi EXPANSION2 dan EXPANSION3, meskipun tampaknya tidak diperlukan.
Menggabungkan semuanya, kami memiliki 6 makro ini:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3) #define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e) #define EXPANSION2(a, b, c, d, ...) EXPANSION3(a, b, c, d) #define EXPANSION3(a, b, c, d, ...) d #define ReSeRvEd_JACK x,x,x #define ReSeRvEd_QUEEN x,x
Dan Anda mungkin menggunakannya seperti ini:
int main() { #if JACK_QUEEN_OTHER(USER) == 1 printf("Hello, Jack!\n"); #endif #if JACK_QUEEN_OTHER(USER) == 2 printf("Hello, Queen!\n"); #endif #if JACK_QUEEN_OTHER(USER) == 3 printf("Hello, who are you?\n"); #endif }
Tautan godbolt wajib: https://godbolt.org/z/8WGa19
sumber
Sederhana saja, saya pikir Anda bisa mengatakannya
#define NAME JACK #if NAME == queen
sumber