2008-10-19 5 views
82

जैसा कि मेरे पिछले प्रश्नों में उल्लेख किया गया है, मैं के & आर के माध्यम से काम कर रहा हूं, और वर्तमान में प्रीप्रोसेसर में हूं। — कुछ और रोचक चीजों में से एक जो मुझे सी — सीखने के पहले प्रयासों से पहले कभी नहीं पता था ## प्रीप्रोसेसर ऑपरेटर है। करने के लिए कश्मीर & आर अनुसार:## प्रीप्रोसेसर ऑपरेटर और गॉथस के अनुप्रयोगों पर विचार करने के लिए क्या हैं?

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

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

तो paste(name, 1) टोकन name1 पैदा करता है।

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

उत्तर

44

CrashRpt: का उपयोग करते हुए ##

CrashRpt (दुर्घटना की रिपोर्ट करने के पुस्तकालय) में एक दिलचस्प उपयोग पीछा कर रहा है यूनिकोड करने के लिए मैक्रो मल्टी-बाइट तार कन्वर्ट करने के लिए:

#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__); 

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

__ DATE __ के बगल में रखकर आपको संकलन त्रुटि मिल जाएगी।


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

विंडोज का उपयोग करता है जैसे निम्नलिखित कुछ:

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

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


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

मैं भी देखा है यह कोड में इस्तेमाल किया accessors और संशोधक परिभाषित करने के लिए:

#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

आप संकलन समय पर स्ट्रिंग शाब्दिक जोड़ सकते हैं, आप 'करने के लिए std :: wstring BuildDate = BuildDate अभिव्यक्ति को कम कर सकता चौड़ा (__DATE__) एल "" WIDEN (__ TIME __); 'और पूरी तरह से एक बार में पूरी स्ट्रिंग का निर्माण करें। – user666412

1

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

UNITTEST(test_name) 

फैलता करने के लिए:

void __testframework_test_name() 
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; 
}; 

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

6

यह सभी प्रकार की स्थितियों में उपयोगी है ताकि आप खुद को दोहराने के लिए न करें। निम्नलिखित Emacs स्रोत कोड से एक उदाहरण है। हम पुस्तकालय से कई कार्यों को लोड करना चाहते हैं। फ़ंक्शन "foo" को 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" (और जोखिम गलत वर्तनी उनमें से एक) लिखने के लिए होने नहीं है।

1

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

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

#define LIFECYCLE(name, func) (struct name x = name##_create(); name##_activate(x); func(x); name##_release()) 
बेशक

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

2

मैं इसे सी प्रोग्राम में उपयोग करता हूं ताकि विधियों के एक सेट के लिए प्रोटोटाइप को सही ढंग से लागू करने में मदद मिल सके जो कुछ प्रकार के कॉलिंग सम्मेलन के अनुरूप हो।

SCREEN_HANDLER(activeCall) 

कुछ इस तरह के लिए विस्तारित: एक तरह से, यह सीधे सी में गरीब आदमी का वस्तु उन्मुखीकरण के लिए इस्तेमाल किया जा सकता

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

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

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

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

2

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

0

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

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

या, यदि आपका संकलक समारोह और समारोह का समर्थन नहीं करता:

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

ऊपर "कार्यों" संदेश लॉग करता है और वास्तव में पता चलता है जो समारोह संदेश लॉग इन ।

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

+1

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

+0

आप वास्तव में सही हैं। मुझे ## का उपयोग करने के लिए मूल स्क्रिप्ट ढूंढनी होगी। मुझ पर शर्म आती है, आज कोई कुकी नहीं! – ya23

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)); 

कुछ compilers पर, इस वसीयत उत्पादन अपेक्षित परिणाम:

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

1+2 std::vector 

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

1 + 2 std :: vector 

काफी आधुनिक बनाम जीसीसी के ऑन (> = 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)); 

GCC CPP documentation chapter on concatenation टोकन-पेस्टिंग ऑपरेटर पर अधिक उपयोगी जानकारी है।

+0

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

+3

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

+0

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

2

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

 


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

 
+3

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

4

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

Link

उस प्रश्न का मेरा जवाब दिखाया है कि कैसे छोटे पूर्वप्रक्रमक जादू को लागू करने (उदाहरण के लिए) इस तरह से अपनी गणन परिभाषित करने देता है ...;

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

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

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

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

46

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

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

#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

इस प्रीप्रोसेसर व्यवहार के स्पष्टीकरण के लिए, http://stackoverflow.com/questions/8231966/why-do-i-need-double-layer-of-indirection-for-macros –

+0

@MichaelBurr मैं आपका उत्तर पढ़ रहा था & मुझे एक शंका है। यह कैसे __LINE__ लाइन नंबर प्रिंट कर रहा है? –

+2

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

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) 
संबंधित मुद्दे

 संबंधित मुद्दे