विचार करने के लिए ## प्रीप्रोसेसर ऑपरेटर और गोचा के आवेदन क्या हैं?


87

जैसा कि मेरे पिछले कई सवालों में बताया गया है, मैं K & R के माध्यम से काम कर रहा हूं, और वर्तमान में प्रीप्रोसेसर में हूं। अधिक दिलचस्प चीजों में से एक - सी सीखने के लिए मेरे किसी भी पूर्व प्रयास से पहले मुझे कभी भी पता नहीं था - ##प्रीप्रोसेसर ऑपरेटर है। K & R के अनुसार:

प्रीप्रोसेसर ऑपरेटर ## मैक्रो विस्तार के दौरान वास्तविक तर्कों को समझने का एक तरीका प्रदान करता है। यदि रिप्लेसमेंट टेक्स्ट में एक पैरामीटर समीप है ##, तो पैरामीटर को वास्तविक तर्क द्वारा बदल दिया जाता है, ##और आसपास के सफेद स्थान को हटा दिया जाता है, और परिणाम फिर से स्कैन किया जाता है। उदाहरण के लिए, मैक्रो paste अपने दो तर्क प्रस्तुत करता है:

#define paste(front, back) front ## back

इसलिए paste(name, 1)एक टोकन बनाता है name1

वास्तविक दुनिया में कोई इसका उपयोग कैसे और क्यों करेगा? इसके उपयोग के व्यावहारिक उदाहरण क्या हैं, और क्या विचार करने के लिए गोचर हैं?

जवाबों:


47

क्रैशट्रॉप: मैक्रो मल्टी-बाइट स्ट्रिंग्स को यूनिकोड में परिवर्तित करने के लिए ## का उपयोग करना

क्रैशरप्ट (क्रैश रिपोर्टिंग लाइब्रेरी) में एक दिलचस्प उपयोग निम्नलिखित है:

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
//Note you need a WIDEN2 so that __DATE__ will evaluate first.

यहां वे एक-बाइट-प्रति-चर स्ट्रिंग के बजाय दो-बाइट स्ट्रिंग का उपयोग करना चाहते हैं। यह शायद ऐसा लगता है कि यह वास्तव में व्यर्थ है, लेकिन वे इसे एक अच्छे कारण के लिए करते हैं।

 std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);

वे इसे दूसरे मैक्रो के साथ उपयोग करते हैं जो तारीख और समय के साथ एक स्ट्रिंग लौटाता है।

लाना Lएक के बगल में __ DATE __आप एक संकलन त्रुटि देना होगा।


विंडोज: सामान्य यूनिकोड या मल्टी-बाइट स्ट्रिंग्स के लिए ## का उपयोग करना

विंडोज कुछ इस तरह का उपयोग करता है:

#ifdef  _UNICODE
    #define _T(x)      L ## x
#else
    #define _T(x) x
#endif

और _Tकोड में हर जगह उपयोग किया जाता है


विभिन्न पुस्तकालयों, स्वच्छ गौण और संशोधक नामों के लिए उपयोग करना:

मैंने इसे एक्सेसर्स और मॉडिफायर्स को परिभाषित करने के लिए कोड में भी उपयोग किया है:

#define MYLIB_ACCESSOR(name) (Get##name)
#define MYLIB_MODIFIER(name) (Set##name)

इसी तरह आप किसी अन्य प्रकार के चतुर नाम निर्माण के लिए इसी विधि का उपयोग कर सकते हैं।


विभिन्न पुस्तकालय, एक साथ कई परिवर्तनशील घोषणाएँ करने के लिए इसका उपयोग करते हैं:

#define CREATE_3_VARS(name) name##1, name##2, name##3
int CREATE_3_VARS(myInts);
myInts1 = 13;
myInts2 = 19;
myInts3 = 77;

3
चूंकि आप संकलित समय पर स्ट्रिंग शाब्दिकों को संक्षिप्त कर सकते हैं, आप BuildDate अभिव्यक्ति को कम कर सकते हैं std::wstring BuildDate = WIDEN(__DATE__) L" " WIDEN(__TIME__); और एक ही बार में पूरे स्ट्रिंग का निर्माण कर सकते हैं।
user666412

49

जब आप टोकन-पेस्ट (' ##') या स्ट्रिंगिंग (' #') प्रीप्रोसेसिंग ऑपरेटरों का उपयोग कर रहे हों, तो एक बात का ध्यान रखें कि आपको सभी मामलों में ठीक से काम करने के लिए उनके लिए एक अतिरिक्त स्तर का उपयोग करना होगा।

यदि आप ऐसा नहीं करते हैं और टोकन-चिपकाने वाले ऑपरेटर को दिए गए आइटम स्वयं मैक्रोज़ हैं, तो आपको ऐसे परिणाम मिलेंगे जो संभवतः आपके लिए नहीं हैं:

#include <stdio.h>

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x

#define SOME_MACRO function_name

int main() 
{
    printf( "buggy results:\n");
    printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));

    printf( "\n" "desired result:\n");
    printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}

उत्पादन:

buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)

desired result:
function_name21

1
इस प्रीप्रोसेसर व्यवहार की व्याख्या के लिए, stackoverflow.com/questions/8231966/…
एडम डेविस

@MichaelBurr मैं आपका उत्तर पढ़ रहा था और मुझे संदेह है। यह लाइन लाइन नंबर कैसे प्रिंट कर रही है?
पीएलजेड

3
@ अभिमन्यु आर्यन: मुझे यकीन नहीं है कि यह वही है जो आप पूछ रहे हैं, लेकिन __LINE__एक विशेष मैक्रो नाम है जिसे प्रीप्रोसेसर द्वारा स्रोत फ़ाइल की वर्तमान पंक्ति संख्या के साथ बदल दिया गया है।
माइकल बुर

यह अच्छा होगा यदि भाषा के विनिर्देशों का हवाला दिया जा सकता है / लिंक किया जा सकता है, जैसा कि यहां है
एंटोनियो

14

यहाँ एक गोचरा है जिसे मैं संकलक के नए संस्करण में अपग्रेड करते समय चलाता था:

टोकन-पेस्टिंग ऑपरेटर ( ##) का अनावश्यक उपयोग गैर-पोर्टेबल है और अवांछित व्हाट्सएप, चेतावनी या त्रुटियां उत्पन्न कर सकता है।

जब टोकन-चिपकाने वाले ऑपरेटर का परिणाम मान्य प्रीप्रोसेसर टोकन नहीं है, तो टोकन-चिपकाने वाला ऑपरेटर अनावश्यक और संभवतः हानिकारक है।

उदाहरण के लिए, कोई टोकन-चिपकाने वाले ऑपरेटर का उपयोग करके संकलन समय पर स्ट्रिंग शाब्दिक निर्माण करने का प्रयास कर सकता है:

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a##+##b)
#define NS(a, b) STRINGIFY(a##::##b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

कुछ संकलक पर, यह अपेक्षित परिणाम का उत्पादन करेगा:

1+2 std::vector

अन्य संकलक पर, इसमें अवांछित व्हाट्सएप शामिल होगा:

1 + 2 std :: vector

GCC के आधुनिक रूप से आधुनिक संस्करण (> = 3.3 या तो) इस कोड को संकलित करने में विफल रहेंगे:

foo.cpp:16:1: pasting "1" and "+" does not give a valid preprocessing token
foo.cpp:16:1: pasting "+" and "2" does not give a valid preprocessing token
foo.cpp:16:1: pasting "std" and "::" does not give a valid preprocessing token
foo.cpp:16:1: pasting "::" and "vector" does not give a valid preprocessing token

समाधान टोकन-चिपकाने वाले ऑपरेटर को छोड़ना है, जब प्रीप्रोसेसर टोकन को C / C ++ ऑपरेटरों तक पहुंचाना है:

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a+b)
#define NS(a, b) STRINGIFY(a::b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

संयोजन पर जीसीसी सीपीपी प्रलेखन अध्याय टोकन-पेस्ट ऑपरेटर पर और अधिक उपयोगी जानकारी है।


धन्यवाद - मुझे इसकी जानकारी नहीं थी (लेकिन फिर मैं इन प्रीप्रोसेसिंग ऑपरेटरों का बहुत अधिक उपयोग नहीं करता ...)।
माइकल बूर

3
इसे एक कारण के लिए "टोकन चिपकाने" ऑपरेटर कहा जाता है - जब आप पूरा कर लेते हैं तो इरादा एक ही टोकन के साथ समाप्त होता है। अच्छा लिख ​​दिया।
मार्क रैनसम

जब टोकन-चिपकाने वाले ऑपरेटर का परिणाम मान्य प्रीप्रोसेसर टोकन नहीं है, तो व्यवहार अपरिभाषित है।
ऐलेकोव

हेक्साडेसिमल फ़्लोट्स, या (C ++ में) अंक विभाजकों और उपयोगकर्ता-परिभाषित शाब्दिकों की तरह भाषा परिवर्तन, लगातार एक "मान्य प्रीप्रोसेसिंग टोकन" का निर्माण करता है, इसलिए कृपया इसे कभी भी दुरुपयोग न करें ! यदि आपको (भाषा को उचित) अलग करना है, तो कृपया उन्हें दो अलग-अलग टोकन के रूप में लिखें, और प्रीप्रोसेसर व्याकरण और भाषा के बीच आकस्मिक बातचीत पर निर्भर न रहें।
केरेक एसबी

6

यह सभी प्रकार की परिस्थितियों में उपयोगी है ताकि आप अपने आप को अनावश्यक रूप से न दोहराएं। निम्नलिखित Emacs स्रोत कोड से एक उदाहरण है। हम एक पुस्तकालय से कई कार्यों को लोड करना चाहते हैं। फ़ंक्शन "फू" को सौंपा जाना चाहिए fn_foo, और इसी तरह। हम निम्नलिखित मैक्रो को परिभाषित करते हैं:

#define LOAD_IMGLIB_FN(lib,func) {                                      \
    fn_##func = (void *) GetProcAddress (lib, #func);                   \
    if (!fn_##func) return 0;                                           \
  }

हम तब इसका उपयोग कर सकते हैं:

LOAD_IMGLIB_FN (library, XpmFreeAttributes);
LOAD_IMGLIB_FN (library, XpmCreateImageFromBuffer);
LOAD_IMGLIB_FN (library, XpmReadFileToImage);
LOAD_IMGLIB_FN (library, XImageFree);

लाभ दोनों को लिखने के लिए नहीं है fn_XpmFreeAttributesऔर "XpmFreeAttributes"(और उनमें से एक को जोखिम में डालना)।


4

स्टैक ओवरफ्लो पर एक पिछला प्रश्न, त्रुटि-प्रवण रीपिटिंग के बिना गणना करने वाले स्थिरांक के लिए स्ट्रिंग प्रतिनिधित्व उत्पन्न करने की एक चिकनी विधि के लिए कहा गया।

संपर्क

उस प्रश्न के बारे में मेरे जवाब से पता चला कि थोड़ा प्रीप्रोसेसर जादू लागू करने से आप इस तरह (उदाहरण के लिए) अपनी गणना को परिभाषित कर सकते हैं ...;

ENUM_BEGIN( Color )
  ENUM(RED),
  ENUM(GREEN),
  ENUM(BLUE)
ENUM_END( Color )

... इस लाभ के साथ कि मैक्रो विस्तार न केवल एन्यूमरेशन (.h फ़ाइल में) को परिभाषित करता है, यह स्ट्रिंग्स के मिलान सरणी (इन .c फ़ाइल) को भी परिभाषित करता है;

const char *ColorStringTable[] =
{
  "RED",
  "GREEN",
  "BLUE"
};

स्ट्रिंग टेबल का नाम ## ऑपरेटर का उपयोग करके मैक्रो पैरामीटर (यानी रंग) को चिपकाने से आता है। एप्लिकेशन (ट्रिक्स?) इस तरह से हैं जहां # और ## ऑपरेटर अमूल्य हैं।


3

जब आप किसी और चीज़ के साथ मैक्रो मापदंडों को बदलना चाहते हैं तो आप टोकन चिपकाने का उपयोग कर सकते हैं।

इसका उपयोग टेम्पलेट्स के लिए किया जा सकता है:

#define LINKED_LIST(A) struct list##_##A {\
A value; \
struct list##_##A *next; \
};

इस मामले में LINKED_LIST (int) आपको देगा

struct list_int {
int value;
struct list_int *next;
};

इसी तरह आप सूची ट्रैवर्सल के लिए एक फ़ंक्शन टेम्प्लेट लिख सकते हैं।


2

मैं इसे C कार्यक्रमों में सही तरीके से प्रोटोटाइप को लागू करने में मदद करने के लिए उपयोग करता हूं जो कि किसी प्रकार के कॉलिंग कन्वेंशन के अनुरूप होना चाहिए। एक तरह से, इसका उपयोग सीधे सी में गरीब आदमी की वस्तु अभिविन्यास के लिए किया जा सकता है:

SCREEN_HANDLER( activeCall )

इस तरह से कुछ करने के लिए फैलता है:

STATUS activeCall_constructor( HANDLE *pInst )
STATUS activeCall_eventHandler( HANDLE *pInst, TOKEN *pEvent );
STATUS activeCall_destructor( HANDLE *pInst );

जब आप करते हैं तो यह सभी "व्युत्पन्न" वस्तुओं के लिए सही परिमाणीकरण को लागू करता है:

SCREEN_HANDLER( activeCall )
SCREEN_HANDLER( ringingCall )
SCREEN_HANDLER( heldCall )

अपनी हेडर फ़ाइलों में ऊपर आदि। यह रखरखाव के लिए भी उपयोगी है यदि आप भी परिभाषाओं को बदलना चाहते हैं और / या "वस्तुओं" के तरीकों को जोड़ना चाहते हैं।


2

SGlib सी में मूल रूप से ठगना करने के लिए ## का उपयोग करता है क्योंकि कोई फ़ंक्शन ओवरलोडिंग नहीं है, ## का उपयोग उत्पन्न कार्यों के नाम में टाइप नाम को गोंद करने के लिए किया जाता है। यदि मेरे पास list_t नामक सूची प्रकार है, तो मुझे sglib_list_t_concat, और इसी तरह के नाम जैसे कार्य मिलेंगे।


2

मैं इसे एक घर में लुढ़कने के लिए गैर-मानक सी संकलक पर एम्बेडेड के लिए उपयोग करता हूं:



#define ASSERT(exp) if(!(exp)){ \
                      print_to_rs232("Assert failed: " ## #exp );\
                      while(1){} //Let the watchdog kill us 



3
मैं इसे 'गैर-मानक' से मतलब लेता हूं कि संकलक ने स्ट्रिंग चिपकाने का काम नहीं किया, लेकिन टोकन चिपकाने का काम किया - या इसके बिना भी काम किया होगा ##?
PJTraill

1

मैं इसे मैक्रो द्वारा परिभाषित चर के लिए कस्टम उपसर्ग जोड़ने के लिए उपयोग करता हूं। तो कुछ इस तरह:

UNITTEST(test_name)

इसका विस्तार:

void __testframework_test_name ()

1

मुख्य उपयोग तब होता है जब आपका नामकरण सम्मेलन होता है और आप चाहते हैं कि आपका मैक्रो उस नामकरण सम्मेलन का लाभ उठाए। शायद आपके पास तरीकों के कई परिवार हैं: image_create (), image_activate (), और image_release () भी file_create (), file_activate (), file_release (), और mobile_actreat (), mobile_activate () और mobile_release ()।

आप ऑब्जेक्ट जीवनचक्र को संभालने के लिए एक मैक्रो लिख सकते हैं:

#define LIFECYCLE(name, func) (struct name x = name##_create(); name##_activate(x); func(x); name##_release())

बेशक, "वस्तुओं का न्यूनतम संस्करण" एक प्रकार का नामकरण सम्मेलन ही नहीं है जो इस पर लागू होता है - नामकरण सम्मेलनों के विशाल बहुमत नाम बनाने के लिए एक सामान्य उप-स्ट्रिंग का उपयोग करते हैं। यह मुझे नाम (ऊपर के रूप में), या क्षेत्र के नाम, चर नाम, या अन्य कुछ भी कार्य कर सकता है।


1

WinCE में एक महत्वपूर्ण उपयोग:

#define BITFMASK(bit_position) (((1U << (bit_position ## _WIDTH)) - 1) << (bit_position ## _LEFTSHIFT))

रजिस्टर बिट विवरण को परिभाषित करते समय हम निम्नलिखित करते हैं:

#define ADDR_LEFTSHIFT                          0

#define ADDR_WIDTH                              7

और BITFMASK का उपयोग करते समय, बस उपयोग करें:

BITFMASK(ADDR)

0

यह लॉगिंग के लिए बहुत उपयोगी है। तुम कर सकते हो:

#define LOG(msg) log_msg(__function__, ## msg)

या, यदि आपका कंपाइलर फ़ंक्शन और फंक का समर्थन नहीं करता है :

#define LOG(msg) log_msg(__file__, __line__, ## msg)

उपरोक्त "फ़ंक्शंस" संदेश लॉग करता है और दिखाता है कि किस फ़ंक्शन ने एक संदेश लॉग किया है।

मेरा C ++ सिंटैक्स काफी सही नहीं हो सकता है।


1
आप उसके साथ क्या करने की कोशिश कर रहे थे? यह "##" के बिना भी काम करेगा, क्योंकि टोकन-पेस्ट की आवश्यकता नहीं है, "" संदेश "। क्या आप संदेश स्ट्रिंग की कोशिश कर रहे थे? इसके अलावा, FILE और LINE को अपरकेस में होना चाहिए, लोअरकेस में नहीं।
bk1e

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