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