Wednesday, 26 April 2017

C/C++ Preprocessor in marathi

C प्रिप्रोसेसर वर बोलू काही तरी ... 


कोणताही C किंवा C++ प्रोग्रॅम करा ( भले तो कोणत्याही  OS वर करा ) आपण नेहमी सुरवात करतो ती प्रोग्रॅम मधल्या सर्वात पहिल्या लाईन पासून, जी लाईन सुरु होते # symbol असलेल्या ओळी पासून , खरं तर आपल्या प्रोग्रॅम मधला पहिला Characterच '#' आणि तीच लाईन सुरु होते #include<abc.h> किंवा #include<abc> किंवा #include "xyz" किंवा #define XXX वगैरे वगैरे .. आणि ह्या लाईन्स च्या शेवटी सेमी कोलन (;) किंवा कॉमा गिमा नाही तर सरळ नवीन लाईन असते . 

सर Preprocessor म्हणजे काय ? नेमकं काय समजायचं ?? जास्त विचार न करता अस समजायला हरकत नाही कि  हा दुसरा तिसरा काही नसून एक अलग प्रोग्रॅम आहे कि जो कंपायलर रन होण्या आधी स्वतः रन होतो. जेव्हा preprocessor रन होतो तेव्हा तो प्रत्येक कोड फाईलमध्ये वरपासून ते खालपर्यंत directives चा शोध घेत जातो. प्रिप्रोसेसर प्रोग्रॅम तितका स्मार्ट नाही त्याला C/C++ चा syntax काहीही कळत नाही , तो कंपायलर रन होण्या आधी फक्त जिथे जिथे # सापडते तिथे तिथे तिथला text manipulate करायचं काम करतो. मग त्याच आउटपुट कंपायलर ला देतो. आणि हो .. हे करताना तो ओरिजिनल कोड फाईल ला कुठल्याही परिस्थितीत हात लावत नाही उलट हा जे काही करतो ते सर्व तात्पुरत्या मेमरी मधेच करतो आणि काम करून झालं कि ती मेमरी साफ करतो. 

जे काही # symbol पासून सुरु होते आणि त्याच्या शेवटी नवीन लाईन असते (सेमी कोलन किंवा कॉमा बिमा नाही) ते म्हणजेच Directives. 

Directives चे प्रकार बघूया :

Includes :
जेव्हा आपण एखादी फाईल #include करून समाविष्ट करतो तेव्हा preprocessor हा त्याच ओळीच्या जागी त्या हेडर फाईल चा सगळा कोड आपल्या कोड मध्ये कॉपी करतो. ज्या वेळी आपल्याला माहिती असते कि काही गोष्टी बऱ्याच ठिकाणी लागणार आहे त्या वेळी हे खूप कामी येते. 

#include करण्याचे २ मार्ग :
  1. #include <filename> किंवा #include <filename.h> (extension सकट आणि बिना extension मध्ये काय फरक आहे हे आपण पुढे पाहू )इथे हि ओळ preprocessor ला सांगत असते कि बाबारे हि फाईल एका विशिष्ट ठिकाणी ठेवली आहे तिथून घे (हे विशिष्ट ठिकाण म्हणजे OS जिथे C/C++ runtime library च्या सर्व हेडर फाईल ठेवते ) तस आपण नेहमी कंपायलर सोबत आलेल्या C/C++standard library च्या फाईल वापरतो . 
  2. #include "filename" हि ओळ preprocessor ला सांगत असते कि बाबारे जिथे source फाईल ठेऊन आहे त्याच directory मध्ये तुला हि फाईल सापडेल. जर हि फाईल त्याच ठिकाणी नाही सापडली तर तो बाकी ठिकाणी शोध घेतो (include paths किंवा environment variables ). 

Macro defines : 
#define directive हे macro तयार करण्यासाठी वापरतात . एखादा macro तयार करणे म्हणजे एक नियम तयार करणे कि "एखाद्या input sequence (e.g. an identifier) ला एका replacement output sequence (e.g. some text) मध्ये तयार करण".
macros चे सुद्धा २ basic types आहेत : 
  1. object-like macros आणि
  2. function-like macros.

Function-like macros हि functions सारखीच असतात आणि त्याचा उद्देश पण तोच जो object-like macros चा आहे . आपण Function-like macros बघणार नाही आहो कारण ते धोकादायक मानलं जात आणि जे जे काही करत तेच inline functions वापरून करता येत. 
Object-like macros २ प्रकारे करता येतात :
#define identifier
#define identifier substitution_text
पहिल्या प्रकारात कोणतंही substitution_text नाही परंतु दुसऱ्यात आहे आणि हो ह्या ओळी च्या
शेवटी पण सेमी कोलन वगैरे काही नाही.

Object-like macros with substitution text
जेव्हा preprocessor अशा directive बघतो, तेव्हा पुढे वापरलेल्या सर्व ‘identifier’ च्या जागी ‘substitution_text’ जाईल. असे  identifier सहजा पूर्ण capital letters मध्ये लिहिली जातात, जर जास्त शब्द असतील तर underscoreटाकून जोडली जातात.
खालील snippet बघा :
#define MY_FAVORITE_NUMBER 3
std::cout << "My favorite number is: " << MY_FAVORITE_NUMBER << std::endl;
वरील कोड ला preprocessor खालील कोड मध्ये तयार करतो :
std::cout << "My favorite number is: " << 3 << std::endl;
म्हणजेच जेव्हा आपण रन करू त्यावेळी आउटपुट 3 येईल. 

Object-like macros without substitution text
Object-like macros बिना substitution text च सुद्धा तयार करता येतात.
उदाहरणार्थ :
#define USE_FLAG 
असे मॅक्रो तयार केले म्हणजे तुम्ही असं सजून घेतलं असणार कि जिथे जिथे USE_FLAG आला आहे तिथे तिथे रिकामी स्पेस टाकूया. तर मग असं रिकाम्या वापरण्याला काय अर्थ , ह्या directive चा वापर असा नाही केला जात. बघूया याचा वापर कसा केला जातो. याचा वापर मुख्यतो Conditional compilation साठी वापरतात. 
Conditional compilation म्हणजे काय?
समजा आपल्याला कंपायलर ला compile करताना सांगायचं कि "हा नको तो कोड compile कर किंवा तो नको हा कोड compile कर" अस सांगण्यासाठीच जे directives आहे यालाच conditional compilation preprocessor directives म्हणतात. मग ते कोणते उदा.  #ifdef, #ifndef आणि  #endif.
  1. #ifdef : हि directive preprocessor ला चेक करण्याची संधी देते कि बाबा रे XYZ नावाचा मॅक्रो आधीच #define आहे का ?  असेल तर आणि तरच #ifdef आणि #endif च्या ब्लॉक मध्ये जो काही कोड आहे तो सगळा compile कर आणि नसेल तर त्याकडे दुर्लक्ष कर, कोड नाही असे समज.
  2. #ifndef : #ifdef च्या उलट 
  3. #endif  : वरील दोन्ही चा स्कोप संपवत. 


आता खालील कोड चा snippet बघा :
#define PRINT_SUGAT 
#ifdef PRINT_SUGAT
   std::cout << "Sugat" << std::endl;
#endif

#ifdef PRINT_MANKAR
   std::cout << "Mankar" << std::endl;
#endif

इथे PRINT_SUGAT हा मॅक्रो #defined असल्यामुळे , फक्त std::cout << "Sugat" << std::endl;
 हि लाईन compile होईल, PRINT_MANKAR या ब्लॉक मध्ये असलेली होणार नाही. 

समजा फक्त खालील कोड असता 
#ifndef PRINT_MANKAR
   std::cout << "Mankar" << std::endl;
#endif

#define PRINT_MANKAR नसल्यामुळे वरील कोड आरामशीर compile होईल. 
आता अजून एक snippet बघा 
#define PRINT_NUMBER 3
#ifdef PRINT_NUMBER
   std::cout << PRINT_NUMBER  << std::endl;
#endif
इथे प्रश्न पडेल कि वरील कोड खालील फॉरमॅट मध्ये तयार होईल का ??
#ifdef PRINT_NUMBER  // दुसऱ्या directive चा भाग आहे रिप्लेस होणार नाही 
   std::cout << 9 << std::endl;  // हा रिप्लेस होईल कारण नॉर्मल कोड सारखा आहे 
#endif

नाही #ifdef PRINT_NUMBER इथे PRINT_NUMBER हा दुसऱ्या directive चा भाग असल्यामुळे इथे तो replace केल्या जात नाही. 

#defines चा scope 
समजा आपल्याकडे दोन source फाईल्स आहेत, उदा :
A.cpp
 #include <iostream>
 void doSomething()
 {
   #ifdef PRINT
     std::cout << "Printing!";
   #endif
   #ifndef PRINT
      std::cout << "Not printing!";
   #endif
 }

आणि 
B.cpp
void doSomething(); // forward declaration 
int main()
{
    #define PRINT
    doSomething();
    return 0;
}
इथे आउटपुट काय येईल ??
आउटपुट : Not printing! 
आश्यर्य वाटून घ्यायचं कारण नाही कारण #define directive चा scope हा त्या source फाईल पुरताच राहतो. परंतु #define directive हेडर फाईल मध्ये असेल तर मात्र ते multiple source फाईल मध्ये इम्पोर्ट होतात आणि त्यामुळे होणारे तोटे पुढच्या सदरात लक्षात येईलच. 
(Conditional compilation आणि header guards यांचे संबंध खूप जवळचे आहे :P  नवीन शिकणाऱ्याला ते कोड्यात पण टाकू शकतात.. :) )

Header guards वर बोलू काही तरी ..... येतो घेऊन लवकरच .... तोवर परत वाचा प्रिप्रोसेसर वर बोलू काही तरी ... हेडर फाईल्स बद्दल बोलू काही तरी ..:P 





6 comments: