सी प्रीप्रोसेसर के साथ दो बार संक्षिप्त कैसे करें और "arg ## _ ## MACRO" के रूप में एक मैक्रो का विस्तार करें?


152

मैं एक कार्यक्रम लिखने की कोशिश कर रहा हूं, जहां कुछ फ़ंक्शन के नाम इस तरह के मैक्रो के साथ एक निश्चित मैक्रो चर के मूल्य पर निर्भर हैं:

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

int NAME(some_function)(int a);

दुर्भाग्य से, मैक्रो NAME()कि में बदल जाता है

int some_function_VARIABLE(int a);

बजाय

int some_function_3(int a);

तो यह स्पष्ट रूप से इसके बारे में जाने का गलत तरीका है। सौभाग्य से, VARIABLE के लिए अलग-अलग संभावित मानों की संख्या कम है, इसलिए मैं बस एक कर सकता हूं #if VARIABLE == nऔर सभी मामलों को अलग से सूचीबद्ध कर सकता हूं , लेकिन मैं सोच रहा था कि क्या ऐसा करने का कोई चतुर तरीका है।


3
क्या आप वाकई इसके बजाय फ़ंक्शन पॉइंटर्स का उपयोग नहीं करना चाहते हैं?
ग्योर्गी एंड्रेसेक

8
@ मुख्य रूप से - फंक्शन पॉइंटर्स रनटाइम पर काम करते हैं, प्रीप्रोसेसर काम करता है (पहले) संकलन समय पर। एक अंतर है, भले ही दोनों का उपयोग एक ही कार्य के लिए किया जा सकता है।
क्रिस लुत्ज

1
मुद्दा यह है कि इसका उपयोग किस रूप में किया जाता है यह एक तेजी से कम्प्यूटेशनल ज्यामिति पुस्तकालय है .. जो एक निश्चित आयाम के लिए कठोर है। हालांकि, कभी-कभी कोई इसे कुछ अलग आयामों (जैसे, 2 और 3) के साथ उपयोग करने में सक्षम होना चाहेगा और इसलिए किसी को आयाम-निर्भर फ़ंक्शन और प्रकार नामों के साथ कोड उत्पन्न करने के लिए एक आसान तरीका की आवश्यकता होगी। इसके अलावा, कोड एएनएसआई सी में लिखा गया है, इसलिए टेम्प्लेट और विशेषज्ञता के साथ फंकी सी ++ सामान यहां लागू नहीं है।
जे जे।

2
फिर से खोलने के लिए मतदान क्योंकि यह सवाल पुनरावर्ती मैक्रो विस्तार और stackoverflow.com/questions/216875/use-in-macros के बारे में विशिष्ट है एक सामान्य "क्या यह अच्छा है"। इस प्रश्न का शीर्षक अधिक सटीक होना चाहिए।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '31

काश, यह उदाहरण कम से कम हो गया होता: एक ही होता है #define A 0 \n #define M a ## A: दो ##का होना महत्वपूर्ण नहीं है।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

जवाबों:


223

मानक सी प्रीप्रोसेसर

$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y)  PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)

extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"





extern void mine_3(char *x);
$

अप्रत्यक्ष के दो स्तर

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

C99 मानक के खंड 6.10.3 में 'मैक्रो रिप्लेसमेंट' शामिल है, और 6.10.3.1 में 'तर्क प्रतिस्थापन' शामिल है।

फ़ंक्शन-जैसे मैक्रो के आह्वान के लिए तर्कों की पहचान होने के बाद, तर्क प्रतिस्थापन होता है। प्रतिस्थापन सूची में एक पैरामीटर, जब तक कि पूर्व #या एक ##टोकन से पहले या उसके बाद एक ##प्रीप्रोसेसिंग टोकन (नीचे देखें) के बाद, सभी मैक्रो के विस्तार के बाद संबंधित तर्क द्वारा प्रतिस्थापित किया जाता है। प्रतिस्थापित किए जाने से पहले, प्रत्येक तर्क के प्रीप्रोसेसिंग टोकन को पूरी तरह से मैक्रो बदल दिया जाता है जैसे कि उन्होंने बाकी प्रीप्रोसेसिंग फ़ाइल बनाई; कोई अन्य प्रीप्रोसेसिंग टोकन उपलब्ध नहीं हैं।

आह्वान में NAME(mine), तर्क 'मेरा' है; यह पूरी तरह से 'मेरा' तक विस्तारित है; फिर इसे प्रतिस्थापन स्ट्रिंग में प्रतिस्थापित किया जाता है:

EVALUATOR(mine, VARIABLE)

अब मैक्रो EVALUATOR की खोज की गई है, और तर्कों को 'मेरा' और 'वार्ले' के रूप में अलग किया गया है; बाद को पूरी तरह से '3' तक विस्तारित किया जाता है, और प्रतिस्थापन स्ट्रिंग में प्रतिस्थापित किया जाता है:

PASTER(mine, 3)

इसका संचालन अन्य नियमों (6.10.3.3 'द ## ऑपरेटर') द्वारा कवर किया गया है:

यदि, फ़ंक्शन-जैसे मैक्रो की प्रतिस्थापन सूची में, एक पैरामीटर तुरंत पूर्ववर्ती है या ##प्रीप्रोसेसिंग टोकन द्वारा पीछा किया जाता है , तो पैरामीटर को संबंधित तर्क के प्रीप्रोसेसिंग टोकन अनुक्रम द्वारा प्रतिस्थापित किया जाता है; [...]

ऑब्जेक्ट-जैसे और फ़ंक्शन-जैसे मैक्रो इनवॉक्शंस के लिए, प्रतिस्थापन सूची से पहले अधिक मैक्रो नामों को बदलने के लिए पुन: नामांकित किया जाता है, प्रतिस्थापन सूची में ##प्रीप्रोसेसिंग टोकन का प्रत्येक उदाहरण (एक तर्क से नहीं) हटा दिया जाता है और पूर्ववर्ती प्रीप्रोसेसिंग टोकन को बदल दिया जाता है निम्नलिखित प्रीप्रोसेसिंग टोकन के साथ।

तो, प्रतिस्थापन सूची होती है xजिसके बाद ##और भी ##द्वारा पीछा किया y; तो हमारे पास:

mine ## _ ## 3

और ##टोकन को खत्म करना और दोनों तरफ के टोकन को 'यी' और '3' के साथ मिलाना है।

mine_3

यह वांछित परिणाम है।


यदि हम मूल प्रश्न को देखते हैं, तो कोड 'some_function' के बजाय 'मेरा' का उपयोग करने के लिए अनुकूलित किया गया था):

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

NAME(mine)

NAME का तर्क स्पष्ट रूप से 'मेरा' है और यह पूरी तरह से विस्तारित है।
6.10.3.3 के नियमों का पालन, हम पाते हैं:

mine ## _ ## VARIABLE

जो, जब ##ऑपरेटरों को समाप्त कर दिया जाता है, तो नक्शे:

mine_VARIABLE

जैसा प्रश्न में बताया गया है।


पारंपरिक सी प्रीप्रोसेसर

रॉबर्ट रूजर पूछता है :

क्या पारंपरिक सी प्रीप्रोसेसर के साथ ऐसा करने का कोई तरीका है जिसमें टोकन चिपकाने वाला ऑपरेटर नहीं है ##?

हो सकता है, और शायद नहीं - यह प्रीप्रोसेसर पर निर्भर करता है। मानक प्रीप्रोसेसर के फायदों में से एक यह है कि इसमें यह सुविधा है जो मज़बूती से काम करती है, जबकि पूर्व-मानक प्रीप्रोसेसर के लिए अलग-अलग कार्यान्वयन थे। एक आवश्यकता यह है कि जब प्रीप्रोसेसर किसी टिप्पणी की जगह लेता है, तो यह एक स्थान उत्पन्न नहीं करता है क्योंकि एएनएसआई प्रीप्रोसेसर को करने की आवश्यकता होती है। जीसीसी (6.3.0) सी प्रीप्रोसेसर इस आवश्यकता को पूरा करता है; क्लेकोड 8.2.1 से क्लैंग प्रीप्रोसेसर नहीं।

जब यह काम करता है, यह काम करता है ( x-paste.c):

#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)

extern void NAME(mine)(char *x);

ध्यान दें कि बीच fun,- बीच में कोई स्थान नहीं है VARIABLE- और यह महत्वपूर्ण है क्योंकि यदि मौजूद है, तो इसे आउटपुट पर कॉपी किया जाता है, और आप mine_ 3नाम के रूप में समाप्त होते हैं , जो निश्चित रूप से मान्य नहीं है। (अब, कृपया मेरे बाल वापस कर सकते हैं?)

जीसीसी 6.3.0 के साथ (चल रहा है cpp -traditional x-paste.c), मुझे मिलता है:

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_3(char *x);

XCode 8.2.1 से क्लैंग के साथ, मुझे मिलता है:

# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2





extern void mine _ 3(char *x);

वे रिक्त स्थान सब कुछ खराब कर देते हैं। मैं ध्यान देता हूं कि दोनों प्रीप्रोसेसर सही हैं; अलग-अलग पूर्व-मानक प्रीप्रोसेसर ने दोनों व्यवहारों को प्रदर्शित किया, जिसने पोर्ट कोड की कोशिश करते समय एक बेहद कष्टप्रद और अविश्वसनीय प्रक्रिया को चिपकाने के लिए टोकन बनाया। ##अंकन के साथ मानक मौलिक रूप से सरल है।

ऐसा करने के अन्य तरीके भी हो सकते हैं। हालांकि, यह काम नहीं करता है:

#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)

extern void NAME(mine)(char *x);

जीसीसी उत्पन्न करता है:

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_VARIABLE(char *x);

बंद है, लेकिन कोई पासा नहीं। YMMV, बेशक, आपके द्वारा उपयोग किए जा रहे पूर्व-मानक प्रीप्रोसेसर पर निर्भर करता है। सच कहूं, अगर आप एक प्रीप्रोसेसर के साथ फंस गए हैं जो सहयोग नहीं कर रहा है, तो शायद पूर्व-मानक एक के स्थान पर एक मानक सी प्रीप्रोसेसर का उपयोग करने की व्यवस्था करना अधिक सरल होगा (आमतौर पर संकलक को कॉन्फ़िगर करने का एक तरीका है जो कि तुलना में उचित है बहुत समय बिताने के लिए काम करने का एक तरीका है।


1
हां, यह समस्या को हल करता है। मुझे पता था कि दो स्तरों की पुनरावृत्ति के साथ चाल - मुझे कम से कम एक बार कठोरता के साथ खेलना था - लेकिन यह नहीं पता था कि यह कैसे करना है।
जे जे।

क्या पारंपरिक सी प्रीप्रोसेसर के साथ ऐसा करने का कोई तरीका है जिसमें टोकन संचालक ऑपरेटर ## नहीं है?
रॉबर्ट रूगर

1
@ रॉबर्ट रुगर: यह उत्तर की लंबाई को दोगुना करता है, लेकिन मैंने जानकारी को कवर करने के लिए जोड़ा है cpp -traditional। ध्यान दें कि कोई निश्चित उत्तर नहीं है - यह आपके द्वारा प्राप्त किए गए प्रीप्रोसेसर पर निर्भर करता है।
जोनाथन लेफ्लर

उत्तर के लिए बहुत बहुत धन्यवाद। यह पूरी तरह से महान है! इस बीच मैंने एक और, थोड़ा अलग समाधान भी पाया। देखें यहाँ । यह भी समस्या है कि यह हालांकि क्लैंग के साथ काम नहीं करता है। सौभाग्य से यह मेरे आवेदन के लिए एक मुद्दा नहीं है ...
रॉबर्ट रेजर

32
#define VARIABLE 3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun) NAME1(fun,VARIABLE)

int NAME(some_function)(int a);

ईमानदारी से, आप यह जानना नहीं चाहते हैं कि यह क्यों काम करता है। यदि आप जानते हैं कि यह क्यों काम करता है, तो आप उस आदमी को काम में लाएँगे जो इस प्रकार की बात जानता है, और हर कोई आपसे प्रश्न पूछेगा। =)

संपादित करें: यदि आप वास्तव में जानना चाहते हैं कि यह क्यों काम करता है, तो मैं खुशी से एक स्पष्टीकरण पोस्ट करूंगा, यह मानते हुए कि कोई भी मुझे इसके लिए नहीं धड़कता है।


क्या आप बता सकते हैं कि इसे अप्रत्यक्ष के दो स्तरों की आवश्यकता क्यों है। मेरे पास पुनर्निर्देशन के एक स्तर के साथ एक उत्तर था लेकिन मैंने उत्तर हटा दिया क्योंकि मुझे अपने विजुअल स्टूडियो में C ++ स्थापित करना था और फिर यह काम नहीं करेगा।
काडे रॉक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.