ฉันต้องทำสิ่งนี้ใน C มันใช้ได้ก็ต่อเมื่อฉันใช้ถ่าน แต่ฉันต้องการสตริง ฉันจะทำเช่นนี้ได้อย่างไร?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
ฉันต้องทำสิ่งนี้ใน C มันใช้ได้ก็ต่อเมื่อฉันใช้ถ่าน แต่ฉันต้องการสตริง ฉันจะทำเช่นนี้ได้อย่างไร?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
คำตอบ:
ฉันไม่คิดว่าจะมีวิธีเปรียบเทียบสตริงความยาวตัวแปรได้อย่างสมบูรณ์ในคำสั่งพรีโปรเซสเซอร์ คุณอาจทำสิ่งต่อไปนี้ได้:
#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
หรือคุณสามารถ refactor รหัสเล็กน้อยและใช้รหัส C แทน
#define USER_VS (3 - USER)
ในกรณีเฉพาะนี้ :)
[อัพเดท: 2018.05.03]
CAVEAT : ไม่ใช่ทุกคอมไพเลอร์ที่ใช้ข้อกำหนด C ++ 11 ในลักษณะเดียวกัน โค้ดด้านล่างใช้งานได้ในคอมไพเลอร์ที่ฉันทดสอบในขณะที่ผู้แสดงความคิดเห็นหลายคนใช้คอมไพเลอร์อื่น
อ้างจากคำตอบของ Shafik Yaghmour ที่: การคำนวณความยาวของสตริง C ในเวลาคอมไพล์ นี่คือ constexpr จริงๆหรือ?
ไม่รับประกันว่านิพจน์คงที่จะได้รับการประเมินในเวลาคอมไพล์เรามีเพียงคำพูดที่ไม่ใช่บรรทัดฐานจากร่างมาตรฐาน C ++ ส่วน 5.19 นิพจน์คงที่ซึ่งระบุว่าสิ่งนี้แม้ว่า:
[... ]> [หมายเหตุ: นิพจน์คงที่สามารถประเมินได้ระหว่างการแปล - หมายเหตุตอนท้าย]
คำนั้นcan
สร้างความแตกต่างในโลก
ดังนั้น YMMV สำหรับคำตอบนี้ (หรือใด ๆ ) ที่เกี่ยวข้องconstexpr
ขึ้นอยู่กับการตีความข้อมูลจำเพาะของผู้เขียนคอมไพเลอร์
[อัพเดท 2016.01.31]
เนื่องจากบางคนไม่ชอบคำตอบก่อนหน้านี้ของฉันเพราะมันหลีกเลี่ยงcompile time string compare
แง่มุมทั้งหมดของ OP โดยการบรรลุเป้าหมายโดยไม่จำเป็นต้องมีการเปรียบเทียบสตริงนี่คือคำตอบโดยละเอียด
คุณทำไม่ได้! ไม่อยู่ใน C98 หรือ C99 ไม่ได้อยู่ใน C11 การปรับแต่ง MACRO จะไม่เปลี่ยนแปลงสิ่งนี้
คำจำกัดความของconst-expression
ใช้ใน#if
ไม่อนุญาตสตริง
อนุญาตให้ใช้อักขระดังนั้นหากคุณ จำกัด ตัวเองเป็นอักขระคุณอาจใช้สิ่งนี้:
#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
คุณสามารถ! ใน C ++ 11. หากคุณกำหนดฟังก์ชันตัวช่วยเวลาคอมไพล์สำหรับการเปรียบเทียบ
// 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
ดังนั้นในที่สุดคุณจะต้องเปลี่ยนวิธีที่คุณ accomlish เป้าหมายของคุณในการเลือกค่าสตริงสุดท้ายและUSER
USER_VS
คุณไม่สามารถทำการเปรียบเทียบสตริงเวลาใน C99 ได้ แต่คุณสามารถรวบรวมเวลาในการเลือกสตริงได้
หากคุณต้องทำการเปรียบเทียบเวลาต่อยจริงๆคุณต้องเปลี่ยนเป็น C ++ 11 หรือตัวแปรที่ใหม่กว่าที่อนุญาตให้ใช้คุณลักษณะนั้นได้
[ตามคำตอบเดิม]
ลอง:
#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
อัปเดต: การวางโทเค็น ANSI บางครั้งก็น้อยกว่าที่ชัดเจน ;-D
การใส่ซิงเกิล#
ก่อนมาโครจะทำให้มาโครเปลี่ยนเป็นสตริงของค่าแทนค่าเปล่า
การใส่คู่##
ระหว่างสองโทเค็นจะทำให้โทเค็นเชื่อมต่อกันเป็นโทเค็นเดียว
ดังนั้นมาโครUSER_VS
จึงมีการขยายjack_VS
หรือqueen_VS
ขึ้นอยู่กับว่าคุณตั้งค่าUSER
อย่างไร
แมโครstringifyS(...)
ใช้การกำหนดทิศทางแมโครดังนั้นค่าของมาโครที่ตั้งชื่อจะถูกแปลงเป็นสตริง แทนชื่อของมาโคร
ดังนั้นจึงUSER##_VS
กลายเป็นjack_VS
(หรือqueen_VS
) ขึ้นอยู่กับว่าคุณตั้งค่าUSER
อย่างไร
ต่อมาเมื่อstringifyแมโครจะถูกใช้เป็นS(USER_VS)
ค่าของUSER_VS
( jack_VS
ในตัวอย่างนี้) จะถูกส่งผ่านไปยังขั้นตอนตรงS_(jack_VS)
ซึ่งจะแปลงความคุ้มค่า ( queen
) "queen"
เป็นสตริง
หากคุณตั้งUSER
ไปแล้วผลสุดท้ายคือสตริงqueen
"jack"
สำหรับการต่อโทเค็นโปรดดู: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
สำหรับการแปลงสตริงโทเค็นโปรดดู: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
[อัปเดต 2015.02.15 เพื่อแก้ไขการพิมพ์ผิด]
#if 0 == c_strcmp( USER, JACK )
เป็นconstexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
ตัวอย่างของคุณใช้งานได้เพราะ USER เป็น JACK เท่านั้น ถ้า USER เป็น QUEEN ก็จะพูดUSER IS QUEEN
และUSER_VS IS QUEEN
constexpr
) จากคำสั่งพรีโปรเซสเซอร์
ต่อไปนี้ใช้ได้ผลกับฉันด้วยเสียงดัง อนุญาตให้สิ่งที่ปรากฏเป็นการเปรียบเทียบค่ามาโครเชิงสัญลักษณ์ #error xxxเป็นเพียงการดูว่าคอมไพเลอร์ทำอะไรได้บ้าง การแทนที่นิยามcatด้วย#define cat (a, b) a ## bแบ่งสิ่งต่างๆ
#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
ใช้ค่าตัวเลขแทนสตริง
สุดท้ายในการแปลงค่าคงที่ JACK หรือ QUEEN เป็นสตริงให้ใช้ตัวดำเนินการ stringize (และ / หรือ tokenize)
ตามที่ระบุไว้ข้างต้นตัวประมวลผลล่วงหน้า ISO-C11 ไม่รองรับการเปรียบเทียบสตริง อย่างไรก็ตามปัญหาในการกำหนดมาโครด้วย "ค่าตรงกันข้าม" สามารถแก้ไขได้ด้วย "การวางโทเค็น" และ "การเข้าถึงตาราง" โซลูชันมาโครเชื่อมต่อ / stringify อย่างง่ายของ Jesse ล้มเหลวด้วย gcc 5.4.0 เนื่องจากการทำให้สตริงเสร็จสิ้นก่อนการประเมินการเรียงต่อกัน (สอดคล้องกับ ISO C11) อย่างไรก็ตามสามารถแก้ไขได้:
#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)
บรรทัดแรก (มาโครP_()
) จะเพิ่มทิศทางเดียวเพื่อให้บรรทัดถัดไป (มาโครVS()
) เสร็จสิ้นการต่อกันก่อนที่จะสตริง (ดูเหตุใดฉันจึงต้องมีการกำหนดทิศทาง สองชั้นสำหรับมาโคร ) มาโครการสตริง ( S()
และS_()
) มาจาก Jesse
ตาราง (มาโครjack_VS
และqueen_VS
) ซึ่งง่ายต่อการบำรุงรักษามากกว่าการสร้าง OP จากเจสซี่
ในที่สุดบล็อกสี่บรรทัดถัดไปจะเรียกใช้มาโครสไตล์ฟังก์ชัน บล็อกสี่บรรทัดสุดท้ายมาจากคำตอบของเจสซี่
การจัดเก็บรหัสfoo.c
และเรียกใช้ตัวประมวลgcc -nostdinc -E foo.c
ผลล่วงหน้าให้ผลตอบแทน:
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"
"jack"
"USER_VS"
ผลลัพธ์เป็นไปตามที่คาดไว้ บรรทัดสุดท้ายแสดงว่าUSER_VS
มาโครไม่ได้ขยายก่อนการสตริง
#if (S(USER)=="jack")
- ฉันได้รับข้อผิดพลาดของตัวประมวลผลล่วงหน้าเมื่อใช้"
- error: invalid token at start of a preprocessor expression
.
หากสตริงของคุณรวบรวมค่าคงที่ของเวลา (เช่นในกรณีของคุณ) คุณสามารถใช้เคล็ดลับต่อไปนี้:
#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
คอมไพลเลอร์สามารถบอกผลลัพธ์ของ strcmp ล่วงหน้าและจะแทนที่ strcmp ด้วยผลลัพธ์ดังนั้นจึงให้ #define ที่สามารถเปรียบเทียบกับคำสั่งพรีโปรเซสเซอร์ได้ ฉันไม่รู้ว่ามีความแปรปรวนระหว่างคอมไพเลอร์ / การพึ่งพาตัวเลือกคอมไพเลอร์หรือไม่ แต่มันใช้ได้กับฉันใน GCC 4.7.2
แก้ไข: จากการตรวจสอบเพิ่มเติมดูเหมือนว่านี่เป็นส่วนขยาย toolchain ไม่ใช่ส่วนขยาย GCC ดังนั้นโปรดพิจารณา ...
$
ส่วนขยายก่อนโปรเซสเซอร์บางประเภทหรือไม่?
คำตอบของPatrickและJesse Chisholmทำให้ฉันทำสิ่งต่อไปนี้:
#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
แทน #define USER 'Q'
#define USER QUEEN
ควรใช้งานได้เช่นกัน แต่ไม่ได้รับการทดสอบ ยังใช้งานได้และอาจจัดการได้ง่ายกว่า
แก้ไข: ตามความคิดเห็นของ @ Jean-François Fabre ฉันปรับคำตอบของฉัน
(s==QUEEN?1:0)
โดย(s==QUEEN)
คุณไม่จำเป็นต้องใช้ ternary ผลลัพธ์ก็คือบูลีนอยู่แล้ว
#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
โดยพื้นฐานแล้วอาร์เรย์ char คงที่ความยาวคงที่จะเริ่มต้นด้วยตนเองแทนที่จะเป็นอาร์เรย์ char แบบคงที่ที่มีความยาวตัวแปรเริ่มต้นโดยอัตโนมัติจะลงท้ายด้วย null ที่สิ้นสุด
คุณไม่สามารถทำได้หาก USER ถูกกำหนดเป็นสตริงที่ยกมา
แต่คุณสามารถทำได้ถ้า USER เป็นแค่ JACK หรือ QUEEN หรือ Joker หรืออะไรก็ตาม
มีสองเทคนิคที่จะใช้:
#define JACK
อะไรเริ่มจาก:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
ตอนนี้ถ้าฉันเขียนJACK_QUEEN_OTHER(USER)
และ USER คือ JACK ตัวประมวลผลล่วงหน้าจะเปลี่ยนเป็นEXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
ขั้นตอนที่สองคือการต่อกัน:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
ตอนนี้JACK_QUEEN_OTHER(USER)
กลายเป็นEXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
สิ่งนี้เปิดโอกาสให้เพิ่มเครื่องหมายจุลภาคจำนวนหนึ่งตามว่าสตริงตรงกันหรือไม่:
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
ถ้า USER เป็น JACK JACK_QUEEN_OTHER(USER)
จะกลายเป็นEXPANSION2(x,x,x, 1, 2, 3)
ถ้า USER เป็น QUEEN JACK_QUEEN_OTHER(USER)
จะกลายเป็นEXPANSION2(x,x, 1, 2, 3)
ถ้า USER เป็นคนอื่นJACK_QUEEN_OTHER(USER)
จะกลายเป็นEXPANSION2(ReSeRvEd_other, 1, 2, 3)
ณ จุดนี้มีบางสิ่งที่สำคัญเกิดขึ้น: อาร์กิวเมนต์ที่สี่ของมาโคร EXPANSION2 เป็น 1, 2 หรือ 3 ขึ้นอยู่กับว่าอาร์กิวเมนต์เดิมที่ส่งผ่านคือแจ็คควีนหรือสิ่งอื่นใด ดังนั้นสิ่งที่เราต้องทำคือหยิบมันออกมา ด้วยเหตุผลที่ยืดยาวเราจะต้องใช้มาโครสองตัวในขั้นตอนสุดท้าย มันจะเป็น EXPANSION2 และ EXPANSION3 แม้ว่าจะดูเหมือนว่าไม่จำเป็นก็ตาม
เมื่อรวมทุกอย่างเข้าด้วยกันเรามี 6 มาโครเหล่านี้:
#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
และคุณอาจใช้สิ่งเหล่านี้:
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
}
ลิงค์ Godbolt บังคับ: https://godbolt.org/z/8WGa19
เป็นเรื่องง่ายที่ฉันคิดว่าคุณสามารถพูดได้
#define NAME JACK
#if NAME == queen