Search

1.10 — Preprocessor और header guards पर एक नज़र

Preprocessor को समझने का सबसे अच्छा तरीका यही होगा, की आप इसे एक बिलकुल अलग program के रूप में देखें, जो आपके program के compile होते वक़्त आपके compiler से पहले run करता है । Preprocessor का तात्पर्य directives को process करने से है । Directives विशेष तरह के instructions होते हैं, जो # symbol से शुरू होकर एक new line पे ख़त्म होते हैं (ध्यान दे, new line, ना की semicolon) । Directives के कई अलग-अलग प्रकार हैं, जिसके बारे में हम अभी चर्चा करने वाले हैं । Preprocessor बहुत ज्यादा smart नहीं होता -- इसलिए ये C++ के syntax को समझने में असमर्थ है; फिर भी, ये compiler के run होने से पहले texts को बेहतर ढंग से प्रस्तुत करने के लिए उपयोगी है ।

Includes

आप #include directive को पहले ही देख चुके हैं । जब आप किसी file को #include करते हैं, preprocessor included file (जिसे include करना है, उदाहरण के लिए #include ) को including file (जिसमे include करना है, उदाहरण के लिए main.cpp) में, inclusion (जहाँ #include directive का प्रयोग होता है) की जगह पर copy कर लेता है । ये तब उपयोगी साबित होता है जब आपको कुछ information को एक से ज्यादा (.cpp या header) files में include करना होता है (ज्यादातर ऐसी चीजें forward declarations ही होती हैं ) ।

#include command को दो तरह से लिखा जा सकता है:

#include , ये style preprocessor को headers ढूढने के लिए उन directories पर जाने के लिए कहता है जिसे विशेष रूप से operating system ने, C++ runtime library के header file store करने के लिए define किया है ।

#include "filename", ये style preprocessor को header files ढूंढने के लिए उन directories में जाने की आज्ञा देता है, जहाँ आपका source file (जिसमेआपने #include directive का प्रयोग किया है) मौजूद है । यदि इसे वहाँ ऐसा कोई header file नहीं मिलता है, तो इसके बाद इसे खोजने के लिए ये उन include paths में चला जाता है, जिसे आपने अपने compiler/IDE की सेटिंग के रूप में specify किया था । यदि यहाँ भी कुछ नहीं मिला, तो ये उस header को बिलकुल angled bracket में बंद header files की तरह treat करेगा ।

Macro defines

#define directive का प्रयोग macros define करने के लिए किया जाता है ।Macro एक ऐसी पद्धति है, जो ये सुनिश्चित करता है की कोई input sequence (जैसे की, कोई identifier) किसी output sequence (जैसे की, कोई text) में कैसे convert किया जाता है । परेशान मत होइए, आप उदाहरण के द्वारा इसे और बेहतर तरीके से समझ पाएंगे ।

Macros मुख्यतः दो प्रकार के होते हैं: object-like macros, और function-like macros ।

Function-like macros बिलकुल functions की ही तरह होते हैं, और उनका इस्तेमाल भी function की ही तरह होता है । हम इसके बारे में यहाँ और ज्यादा चर्चा नहीं करेंगे क्यूंकि साधारणतः, function like macros programming के दृष्टिकोण से खतरनाक माने जाते हैं, अर्थात इनका उपयोग करने से आपको मुश्किलों का सामना करना पड़ सकता है । इसके द्वारा किये जाने वाले लगभग सभी काम (inline) functions की सहायता से किया जा सकता है, वो भी बिना किसी परेशानी के ।

अब बात करते हैं, object-like macros की । Object-like macros को दो भागों में बाँटा जा सकता है:

#define identifier
#define identifier substitution_text

ऊपर वाले definition में किसी तरह के “substitution text” का इस्तेमाल नहीं किया गया है, लेकिन ध्यान दीजिये, दुसरे definition में हमने इसका उपयोग किया है । क्यूंकि ये preprocessor declarations हैं, ध्यान दीजिये की इनमे से कोई भी semicolon के साथ ख़त्म नहीं होता ।

Substitution text के साथ Object like macros

जब भी preprocessor को ये directive दिखाई देता है, ये इसके बाद हर जगह ‘identifier’ को इसके ‘substitution_text’ से replace कर देता है । पहले से ही identifiers को capital letter (जैसे A) में लिखने की पद्धति चली आ रही है, इसलिए हमने भी यहाँ उसी style को अपनाया है ।

इस code snippet पर एक नज़र डालें:

Preprocessor इसे कुछ इस तरह बदल देगा:

और जब आप इसे run करोगे, आपको My favorite number is: 9 print हुआ मिलेगा ।

हम इस घटना पर और अधिक चर्चा करेंगे (और ये भी बताएँगे की आपको इस तरीके का प्रयोग क्यूँ नहीं करना चाहिए), लेकिन वो सब section 2.8 -- Literals, symbolic constants, और const variables में ।

Substitution text के बिना Object-like macros

Object-like macros को बिना किसी substitution text के भी define किया जा सकता है ।

उदाहरण के लिए:

इस तरह के macros बिलकुल वैसा ही behave करेंगे जैसा आप सोच रहे हो: जब preprocessor इस identifier को देखेगा, ये इसे किसी के साथ replace या remove नहीं करेगा ।

यहाँ तक आपको ये style (Object-like macros, बिना substitution text) बिलकुल बेकार लगा होगा, लेकिन असल में होता यूँ है, की इस style का प्रयोग, substitution text वाले style से ज्यादा किया जाता है । हम अगले section (conditional compilation) में इसका कारण जानेंगे ।

Object-like macros जो substitution text के साथ होते हैं, उनसे अलग बिना substitution text वाले macros उपयोग के लिए बेहतर माने जाते हैं ।

Conditional compilation

Conditional compilation preprocessor directives आपको ये निश्चित करने की अनुमति देता है की किस condition (अवस्था/हालात) में लिखा गया code compile करना है, और कब नहीं करना है । हम इस section में केवल तीन conditional compilation directives के बारे में जानेंगे, जो हैं: #ifdef, #ifndef, और #endif ।

#ifdef preprocessor directive preprocessor को, ये check करने की आज्ञा देता है, की क्या कोई value पहले define किया गया है । यदि value पहले से #defined है, तो #ifdef और इसके संगत #endif के बीच का code compile होगा । यदि value पहले से #defined नहीं है, तो इन दोनों के बीच लिखा गया code ignore कर दिया जायेगा ।

Code के इस snippet पे नज़र डाले:

यहाँ, क्यूंकि PRINT_JOE पहले #define किया गया है, line cout << "Joe" << endl; compile हो जायेगा । ठीक इसके विपरीत, क्यूंकि PRINT_BOB कही भी #define नहीं किया गया है, line cout << "Bob" << endl; compile नहीं होगा ।

#ifndef, #ifdef का opposite (उल्टा) है, ये preprocessor को ये check करने की आज्ञा देता है, की क्या कोई value पहले से #define ' नहीं ' किया गया है ।

ये program "Bob" print करेगा, क्यूंकि PRINT_BOB कहीं भी #defined नहीं है ।

Header guards

Header files, दुसरे header files को भी #include कर सकते हैं, और इसी कारण से ये संभव है की कोई header file किसी source file में एक से ज्यादा बार #include हो जाये । उदाहरण के लिए, ज़रा इसे देखें:

mymath.h:

add.h:

subtract.h:

main.cpp:

यहाँ, जब हम add.h को include करते हैं, ये mymath.h और add के prototype, दोनों को including code file में शामिल कर लेता है । अब जब हम subtract.h को include करते हैं, ये mymath.h (फिर से) और subtract() के prototype को code file में शामिल कर लेता है । फलस्वरूप, mymath.h के contents code file (main.cpp) में दो बार include हो जाते हैं । अब यहाँ ध्यान दीजिये, यदि mymath.h के सारे contents including file में दो बार include हो जायेंगे, तो इसका मतलब है की function cardsInDeck() भी including file में दो बार define हो जायेगा, और इसी के कारण compiler इस function के एक से ज्यादा बार define होने का error देगा ।

इस परेशानी से बचने के लिए हम header guards का प्रयोग करते हैं, जो असल में conditional compilation directives हैं और इन्हें कुछ इस प्रकार लिखा जाता है:

अब जब ये header include किया जायेगा, तो सबसे पहले preprocessor ये check करेगा की क्या SOME_UNIQUE_NAME_HERE पहले कहीं define किया गया है ! यदि हमने पहली बार SOME_UNIQUE_NAME_HERE को include किया है, तो preprocessor इसे समझ जायेगा और SOME_UNIQUE_NAME_HERE define कर लिया जायेगा । फलस्वरूप, इस file के सारे contents को including file में include कर लिया जायेगा । इसके विपरीत, यदि SOME_UNIQUE_NAME_HERE पहले define किया गया है, तो इसका मतलब है की इसके सारे contents code file में पहले से ही included हैं । Preprocessor इसे समझ लेगा और SOME_UNIQUE_NAME_HERE के सारे contents इस मर्तबा ignore कर दिए जायेंगे ।

आपके हर header file में header guard होना चाहिए । SOME_UNIQUE_NAME_HERE आपके पसंद का कोई भी नाम हो सकता है, लेकिन जो भी हो, .h लगा नाम (अर्थात आपके header file का नाम) इस्तेमाल के लिए बेहतर माना जाता है। उदाहरण के लिए, add.h का header file कुछ इस तरह से लिखा जायेगा:

Standard library भी अपने header files के लिए header guards का प्रयोग करता है । यदि आप Visual Studio 2005 Express में iostream header file को देखोगे, तो वो कुछ ऐसा दिखाई देगा:

हमारे mymath.h example को header guards लगाकर update करना

आइये हमारे mymath.h example पर वापस लौटते हैं:

mymath.h:

add.h:

subtract.h:

main.cpp:

cardsInDeck() को दो बार include होने से रोकने के लिए, हम अब mymath.h में header guards का प्रयोग करेंगे:

mymath.h, header guards के साथ:

अब, main.cpp, add.h को #include करेगा, जिसके साथ-साथ mymath.h भी #include हो जायेगा । क्यूंकि MYMATH_H पहले कहीं define नहीं किया गया है, mymath.h के contents (जिसमे cardsInDeck() भी शामिल है) code file में define कर लिए जायेंगे और अब MYMATH_H define हो चूका होगा । अब इसके बाद main.cpp subtract.h को #include करेगा और इसमें भी mymath.h include किया गया है, लेकिन क्यूंकि MYMATH_H पहले ही define कर लिया जा चूका है, इस बार mymath.h का सारा code ignore कर दिया जायेगा ।

Header guard को जोड़कर, अब हमने ये पक्का कर लिया है की mymath.h, main.cpp में केवल और केवल एक ही बार #include किया जायेगा ।

Code को और ज्यादा बेहतर बनाने के लिए, add.h और subtract.h में भी header guards का इस्तेमाल किया जा सकता है ।

#pragma once

बहुत सारे modern compilers header guards से बेहतर और आसान तरीका पेश करते हैं, जो बिलकुल header guards की ही तरह काम करता है: -- #pragma directive:

यहाँ #pragma once बिलकुल header guards की तरह काम करेगा, और इसके इस्तेमाल से एक फायदा ये भी है की ये लिखने में आसान है जिसके चलते गलतियाँ होने की सम्भावना घट जाती है । stdafx.h file, जिसे Visual Studio उन projects में include करता है जिनमे precompiled headers इस्तेमाल किये जाते हैं, में pragma directive का प्रयोग देखने को मिल सकता है ।

फिर भी, #pragma once के इस्तेमाल करने या ना करने पर C++ (इसके standards) कोई officially सलाह नहीं देते, और सारे compilers इसे support भी नहीं करते (फिर भी, कई modern compilers इसके लिए supportive हैं) ।

हर compiler में compatibility के लिए, हमारी सलाह है कि आप header guards का ही उपयोग करें ।

सारांश

Header guards, कोई header file किसी source file में दोबारा include ना हो जाये, ये पक्का करने के लिए design किये गए है (इससे code के duplication में कमी आती है) । फिर भी, header guards किसी header file के contents को अलग-अलग project या program में include होने से नहीं रोकता (जैसे की add.h और subtract.h का उपयोग ऊपर दिए गए example के अलावा किसी दूसरे project में भी किया जा सकता है) । ये एक अच्छी चीज़ है क्योंकि कभी-कभी हमें अलग-अलग projects में एक ही तरह की functionalities (या एक ही तरह के contents) की ज़रूरत होती है ।

Quiz

1) ऊपर दिए गए mymath.h example का प्रयोग करते हुए, add.h और subtract.h header files में header guards जोड़ें ।

Quiz Answers

1) Show Solution

1.10a -- आपका पहला program कैसे design किया जाये
Index
1.9 -- Header files

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter