सी सशर्त प्रीप्रोसेसर-निर्देशों में तारों की तुलना कैसे करें


92

मुझे सी में ऐसा कुछ करना है। यह केवल तभी काम करता है जब मैं चार का उपयोग करता हूं, लेकिन मुझे एक स्ट्रिंग की आवश्यकता है। मैं यह कैसे कर सकता हूँ?

#define USER "jack" // jack or queen

#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif

तुम सिर्फ strcmp का उपयोग क्यों नहीं कर सकते?

@Brian: हाँ, मैं भी सवाल :-) पढ़ें। बस यह सुनिश्चित करना चाहता था कि वह जानता था कि स्ट्रैम्प मौजूद है, और प्रतिक्रिया ज्ञानवर्धक हो सकती है, क्योंकि मैं ऐसा करने के लिए कोई कारण नहीं सोच सकता।

2
बस यह उल्लेख करना चाहता था कि एक ही चीज नियमित रूप से कोड के लिए जाती है, न कि सिर्फ प्रीप्रोसेसर। एक स्ट्रिंग का उपयोग कभी न करें जब एक साधारण मूल्य करेगा। स्ट्रिंग्स में पूर्णांक या एनम की तुलना में बहुत अधिक ओवरहेड होता है और यदि आपको उनकी तुलना में अधिक कुछ करने की आवश्यकता नहीं है, तो स्ट्रिंग्स गलत समाधान हैं।
25:10

यह आसान होगा यदि प्रश्न में वांछित बनाम वास्तविक व्यवहार के बारे में थोड़ी अधिक जानकारी शामिल होगी।
ब्रेंट बर्नबर्न

जवाबों:


69

मुझे नहीं लगता कि प्रीप्रोसेसर निर्देशों में पूरी तरह से चर लंबाई स्ट्रिंग तुलना करने का एक तरीका है। आप शायद निम्नलिखित कर सकते हैं:

#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

या आप कोड को थोड़ा रिफलेक्टर कर सकते हैं और इसके बजाय C कोड का उपयोग कर सकते हैं।


3
या वह #define USER_VS (3 - USER)इस विशिष्ट मामले में हो सकता है। :)
जेसी चिशोल्म

17

[अद्यतन: 2018.05.03]

गुफा : सभी कंपाइलर C ++ 11 विनिर्देश को एक ही तरीके से लागू नहीं करते हैं। मेरे द्वारा परीक्षण किए गए कंपाइलर में नीचे कोड काम करता है, जबकि कई टिप्पणीकारों ने एक अलग संकलक का उपयोग किया।

Shafik Yaghmour के जवाब से उद्धृत: संकलन समय पर C स्ट्रिंग की लंबाई। क्या यह वास्तव में एक अड़चन है?

लगातार अभिव्यक्तियों का संकलन समय पर मूल्यांकन किए जाने की गारंटी नहीं है, हमारे पास केवल C ++ मानक खंड 5.19 से लगातार एक गैर-मानक उद्धरण है जो यह कहता है:

[...]> [नोट: अनुवाद के दौरान लगातार अभिव्यक्तियों का मूल्यांकन किया जा सकता है।

यह शब्द canदुनिया में सभी अंतर बनाता है।

तो, इस (या किसी भी) पर YMMV जवाब है constexpr, संकलक लेखक की कल्पना की व्याख्या पर निर्भर करता है।

[अपडेट किया गया 2016.01.31]

जैसा कि कुछ को मेरा पहले वाला उत्तर पसंद नहीं आया क्योंकि यह स्ट्रिंग तुलना की कोई आवश्यकता के साथ लक्ष्य को पूरा करने से ओपी के पूरे पहलू से बचता है compile time string compare, यहां एक अधिक विस्तृत जवाब है।

आप नहीं कर सकते! 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 या नए वेरिएंट को बदलने की आवश्यकता है जो उस सुविधा की अनुमति देते हैं।

[मूल ANSWER FOLLOWS]

प्रयत्न:

#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

Stringify मैक्रो S(...)मैक्रो अविवेक का उपयोग करता है ऐसा नाम मैक्रो का मान एक स्ट्रिंग में परिवर्तित हो जाती। मैक्रो के नाम के बजाय।

इस प्रकार आप कैसे सेट करते हैं, इसके आधार पर (या ) USER##_VSबन जाता है ।jack_VSqueen_VSUSER

बाद में, जब स्ट्रिंग मैक्रो का उपयोग ( इस उदाहरण में) S(USER_VS)के मान के रूप में किया जाता है , तो अप्रत्यक्ष कदम को पारित किया जाता है जो एक स्ट्रिंग में इसके मूल्य ( ) को परिवर्तित करता है ।USER_VSjack_VSS_(jack_VS)queen"queen"

यदि आप सेट USERकरते हैं queenतो अंतिम परिणाम स्ट्रिंग है "jack"

टोकन के लिए, देखें: https://gcc.gnu.org/oniltocs/cpp/Concaten.net.html

टोकन स्ट्रिंग रूपांतरण के लिए, देखें: https://gcc.gnu.org/oniltocs/cpp/Stringification.html#Stringification

[एक टाइपो को ठीक करने के लिए २०१५.०२.१५ अपडेट किया गया।]


5
@JesseChisholm, क्या आपने अपने C ++ 11 संस्करण की जांच की? मैं इसे जीसीसी 4.8.1, 4.9.1, 5.3.0 पर काम नहीं कर सकता। यह {{#if 0 (=}) {{#if 0 == c_strmp / * यहाँ * / (USER, QUEEN)}} पर {{अनुपलब्ध बाइनरी ऑपरेटर को गायब करना}}
दिमित्री एलिसोव

3
@JesseChisholm तो मैं आपके C ++ 11 उदाहरण को संकलित करने में कामयाब रहा, अगर मैं बदल #if 0 == c_strcmp( USER, JACK )जाता हूंconstexpr int comp1 = c_strcmp( USER, JACK ); #if 0 == comp1
दिमित्रि एलिसोव

4
@JesseChisholm, हम्म, अभी भी कोई किस्मत नहीं। कोई भी कॉन्स्ट्रेक्स चर शून्य में बराबर होता है #if। आपका उदाहरण केवल इसलिए काम करता है क्योंकि USER JACK है। अगर USER QUEEN होता, तो यह कहता USER IS QUEENऔरUSER_VS IS QUEEN
दिमित्री Elisov

9
इस जवाब का यह c ++ 11 हिस्सा गलत है। आप constexprप्रीप्रोसेसर निर्देशों से फ़ंक्शन (यहां तक ​​कि ) को कॉल नहीं कर सकते ।
इंटरजय

8
इस फ्लैट-आउट गलत जवाब ने पहले से ही इसे संदर्भित करने वाले किसी व्यक्ति को गुमराह किया है। आप प्रीप्रोसेसर से एक कॉन्स्ट्रेक्स फ़ंक्शन को कॉल नहीं कर सकते हैं; constexpr को अनुवाद चरण तक एक कीवर्ड के रूप में भी नहीं पहचाना जाता है। अनुवाद चरण में प्रीप्रोसेसिंग 4 की जाती है।
एच वाल्टर्स

9

निम्नलिखित ने मेरे लिए क्लैंग के साथ काम किया। अनुमति देता है जो प्रतीकात्मक मैक्रो मूल्य तुलना के रूप में प्रकट होता है। # शेर xxx सिर्फ यह देखने के लिए है कि कंपाइलर वास्तव में क्या करता है। #Define cat (a, b) के साथ बिल्ली की परिभाषा को बदलने से ## 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

यकीन नहीं होता कि यह बुराई, शानदार, या दोनों है, लेकिन यह वही था जो मैं देख रहा था - धन्यवाद! एक और सहायक चाल है 1 से शुरू करने के लिए अपने xUSER_ मैक्रोज़ को परिभाषित करने के लिए। फिर आप उन मामलों को पकड़ने के लिए अपनी #elsif सूची के अंत में एक #else क्लॉज जोड़ सकते हैं जहां USER गलती से किसी ऐसी चीज़ के लिए सेट हो जाता है जिसे आप संभालना नहीं जानते हैं। (अन्यथा अगर आप 0 से संख्या लेते हैं तो 0 मामला आपका कैटचेल बन जाता है, क्योंकि यह अपरिभाषित प्रतीकों के लिए प्रीप्रोसेसर की डिफ़ॉल्ट संख्यात्मक मान है।)
स्लैम करें

8

स्ट्रिंग के बजाय संख्यात्मक मान का उपयोग करें।

अंत में स्थिरांक JACK या QUEEN को एक स्ट्रिंग में बदलने के लिए, stringize (और / या tokenize) ऑपरेटरों का उपयोग करें।


2

जैसा कि पहले ही ऊपर कहा गया है, ISO-C11 प्रीप्रोसेसर स्ट्रिंग तुलना का समर्थन नहीं करता है । हालांकि, "विपरीत मान" के साथ एक मैक्रो असाइन करने की समस्या को "टोकन चिपकाने" और "टेबल एक्सेस" के साथ हल किया जा सकता है। जेसी का सरल कॉन्टेनेट / स्ट्रिफ़ मैक्रो-सॉल्यूशन 5.4.0 gcc के साथ विफल हो जाता है क्योंकि कॉनटाइजेशन (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_()) जेसी से हैं।

मेज (मैक्रोज़ jack_VSऔर queen_VS) जो कि ओपी के निर्माण के बाद की तुलना में बनाए रखना बहुत आसान है, जेसी से है।

अंत में, अगली चार-पंक्ति ब्लॉक फ़ंक्शन-शैली मैक्रोज़ को आमंत्रित करता है। अंतिम चार-पंक्ति ब्लॉक जेसी के उत्तर से है।

कोड को 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मैक्रो है नहीं stringization से पहले का विस्तार किया।


यह काम करता है अच्छी तरह से, जब तक मैं वास्तव में करने की कोशिश की तुलना उत्पन्न स्ट्रिंग, एक सशर्त संकलन करने के लिए: #if (S(USER)=="jack")- मैं एक पूर्वप्रक्रमक त्रुटि का उपयोग करते समय मिलता है "- error: invalid token at start of a preprocessor expression
ysap

1

यदि आपके तार समकालिक स्थिरांक (आपके मामले में) हैं, तो आप निम्नलिखित चाल का उपयोग कर सकते हैं:

#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 दे रहा है जिसकी तुलना प्रीप्रोसेसर निर्देशों से की जा सकती है। मुझे नहीं पता कि कंपाइलर विकल्पों पर कंपाइलरों / निर्भरता के बीच कोई विचरण है, लेकिन यह मेरे लिए जीसीसी 4.7.2 पर काम करता है।

संपादित करें: आगे की जांच पर, ऐसा लगता है कि यह एक टूलकिन एक्सटेंशन है, न कि जीसीसी विस्तार, इसलिए इसे ध्यान में रखें ...


7
यह निश्चित रूप से मानक सी नहीं है, और मैं नहीं देखता कि यह किसी भी संकलक के साथ कैसे काम करेगा। कंपाइलर कभी-कभी अभिव्यक्तियों के परिणाम (यहां तक ​​कि फ़ंक्शन कॉल, यदि वे इनलाइन हैं) बता सकते हैं, लेकिन पूर्व-प्रोसेसर नहीं। क्या आपका $किसी तरह के प्री-प्रोसेसर एक्सटेंशन का उपयोग है?
बदसूरत

3
ऐसा लगता है कि '#if $ USER_JACK == 0' सिंटैक्स काम करता है, कम से कम GNU C ++ के साथ देशी एंड्रॉइड कोड (JNI) बनाने के लिए उपयोग किया जाता है ... मुझे यह नहीं पता था, लेकिन यह बहुत उपयोगी है, हमें बताने के लिए धन्यवाद यह!
gregko

6
मैंने इसे GCC 4.9.1 पर आज़माया, और मुझे विश्वास नहीं है कि यह वही करेगा जो आपको लगता है कि यह करता है। कोड संकलित करते समय, यह आपको अपेक्षित परिणाम नहीं देगा। '$' को एक चर नाम के रूप में माना जाता है। तो पूर्वप्रक्रमक, '$ USER_JACK' चर की तलाश में नहीं है यह खोजने और यह 0. का डिफ़ॉल्ट मान इस प्रकार दे रही है, तो आप हमेशा USER_VS strcmp की परवाह किए बिना USER_QUEEN रूप में परिभाषित किया गया होगा
विटाली

1

पैट्रिक द्वारा और जेसी चिशोल्म द्वारा किए गए जवाब ने मुझे निम्नलिखित कार्य करने के लिए प्रेरित किया:

#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 भी काम करना चाहिए लेकिन परीक्षण नहीं किया गया था काम भी करता है और संभालना आसान हो सकता है।

EDIT: @ जीन-फ्रांकोइस फैबरे की टिप्पणी के अनुसार मैंने अपने उत्तर को अनुकूलित किया।


परिवर्तन (s==QUEEN?1:0)से (s==QUEEN)आप त्रिगुट जरूरत नहीं है, परिणाम पहले से ही एक बूलियन है
जीन फ़्राँस्वा Fabre

0
#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

यह मूल रूप से एक निश्चित लंबाई वाली स्थिर चारित्रिक सरणी है जिसे एक चर लंबाई के बजाय मैन्युअल रूप से आरंभिक रूप से स्थिर किया गया है। स्वचालित रूप से हमेशा समाप्त होने वाले अशक्त चार वर्णों के साथ आरंभिक रूप से समाप्त


0

यदि आप USER को एक उद्धृत स्ट्रिंग के रूप में परिभाषित करते हैं तो आप ऐसा नहीं कर सकते।

लेकिन आप यह कर सकते हैं कि यदि USER सिर्फ JACK या QUEEN या जोकर या जो भी हो।

उपयोग करने के लिए दो तरकीबें हैं:

  1. टोकन-स्प्लिसिंग, जहाँ आप एक पहचानकर्ता को किसी अन्य पहचानकर्ता से मिलाते हैं, केवल उनके पात्रों को संक्षिप्त करके। यह आपको #define JACKकुछ के बिना JACK के खिलाफ तुलना करने की अनुमति देता है
  2. परिवर्तनशील मैक्रो विस्तार, जो आपको मैक्रोज़ को तर्क संख्याओं के चर के साथ संभालने की अनुमति देता है। यह आपको विशिष्ट पहचानकर्ताओं को अल्पविरामों की बदलती संख्या में विस्तारित करने की अनुमति देता है, जो आपकी स्ट्रिंग तुलना बन जाएगा।

तो चलिए शुरू करते हैं:

#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
}

अप्रचलित देवभूमि लिंक: https://godbolt.org/z/8WGa19


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.