Tuesday, 2 May 2017

Header Guards in marathi

हेडर गार्ड वर बोलूया काही तरी .. 


हेडर गार्ड बद्दल सुरवात करण्या आधी आपण थोड डुप्लिकेट डेफिनिशन चे प्रॉब्लेम बघूया. 

डुप्लिकेट डेफिनिशन चे अडथळे :

C/C++ मध्ये एखाद्या identifier ला एक आणि एकच definition असते, त्यामुळे जर कुणी प्रोग्रॅम मध्ये एखादा variable identifier एक पेक्षा जास्त वेळा वापरल्यास कंपायलर लगेच बोंबलतो. याच प्रमाणे जर एखाद्या प्रोग्राम मध्ये एकाच नावाची function एक पेक्षा वेळ define केलेली असल्यास सुद्धा कंपायलर 


int main()
{
    int x; // this is a definition for identifier x
    int x; // compile error: duplicate definition
 
    return 0;
}

याच प्रमाणे जर एखाद्या प्रोग्राम मध्ये एकाच नावाच function एक पेक्षा अनेक वेळ define केल असल्यास सुद्धा कंपायलर लगेच बोंबलतो . उदा :

#include < iostream >

int foo(void){
  return 5;
}

int foo(void){   // कंपायलर लगेच बोंबलेल डुप्लिकेट आहे म्हणून 

  return 5;
}

int main()
{
    std::cout << foo() << endl; 
    return 0;
}

आता कंपायलर बोंबलल्यावर आपण एक function काढून टाकला तर आपला प्रॉब्लेम सुटेल परंतु हेडर फाईल जर एक पेक्षा जास्त वेळ इम्पोर्ट झाल्यास आपल्यावर हि वेळ येईल आणि आपण रडत बसू , मी तर हेडर फाईल एकदाच तर टाकली .... तरी हे function परत आलं कुठून ...कटकट ... हि कटकट तेव्हा येईल जर तीच हेडर फाईल अजून कुठल्या आतमधल्या हेडर फाईल नि स्वतः #include टाकून ठेवली असेल (बरेच दा असच होत , दुसऱ्यांवर अवलंबून राहण्यापेक्षा 'अनेक' हेडर फाईल - 'अनेक' हेडर फाईलला स्वतःच include करून घेतात )अन हे आपल्याला माहित पण नसेल (माहित होईल जर प्रत्येक फाईल आतमध्ये काय काय include करत असेल याचा शोध घेतला तरच.... जो आपण कधीही घेत नाही). 

खालील उदाहरण बघा :

समजा आपल्याकडे maths.h नावाची हेडर फाईल आहे आणि त्यात एक function डेफिनेशन सकट आहे (हेडर फाईल मध्ये डेफिनेशन सकट functions असता काम नये इथे फक्त उदाहरण घेण्यासाठीच लिहिलेलं आहे कृपया असं लिहू नका ) , हि  फाईल geometry.h हेडर फाईल मध्ये सुद्धा इंपोर्टेड आहे आणि main.cpp मध्ये सुद्धा आहे. 

१. math.h:

int getSquareSides()
{
    return 4;
}


२. geometry.h:
#include "maths.h"

३. main.cpp:


#include "maths.h"
#include "geometry.h"

int main(void){
   return 0;
}

या प्रोग्रॅम कडे बघितलं तर काही हि चुकीचं दिसत नाही , परंतु हा प्रोग्रॅम compile होणार नाही. कारण शोधायला गेलं तर एकच सापडेल maths.h मध्ये असलेलं function definition. इथे main.cpp मध्ये पहिल्यांदा "maths.h" इम्पोर्ट झालेलं आहे म्हणजेच getSquareSides() हे function इम्पोर्ट झालं आणि परत geometry.h मध्ये सुद्धा maths.h असल्यामुळे परत getSquareSides() हे function पुन्हा इम्पोर्ट झालं. आता कोड बघितला तर असा दिसेल. 

main.cpp:

int getSquareSides()  //#include "maths.h" मुळे include झालेला 
{
    return 4;
}

int getSquareSides() //#include "geometry.h" मुळे include झालेला 
{
    return 4;
}

int main(void){
   return 0;
}

डुप्लिकेट definition मुळे हा प्रोग्रॅम compile होणार नाही. प्रत्येक हेडर फाईल स्वतंत्ररित्या एकदम व्यवस्थित आहे बरोबर आहे , परंतु आपण main.cpp मध्ये दोन्ही फाईल्स वापरल्यात आणि आपण असे गृहीत धरू कि आपल्याला हे माहित नाही कि geometry.h च्या आत मध्ये maths.h इम्पोर्ट केलं आहे. अशा वेळी आपण डुप्लिकेट definition च्या कचाट्यात सापडणारच. तर मग प्रश्न पडतो कि हा अडथळा सोडवायचा कसा ?

हे सुद्धा लक्षात असुद्या कि नुसतं variables असो कि functions असो किंवा कोणतीही डुप्लिकेट definition असो , असा प्रॉब्लेम येणारच. हा प्रॉब्लेम सोडविण्यासाठीच हेडर गार्ड आहे. 

Header Guards :

हेडर गार्डस हे conditional compilation directive आहे (conditional compilation म्हणजे काय याबद्दल आपण हेडर फाईल्स वर बोलूया काही तरी मध्ये सविस्तर बघितलेलं आहे). याला खालील फॉर्म मध्ये लिहता येत. 


#ifndef SOME_UNIQUE_NAME_HERE
#define SOME_UNIQUE_NAME_HERE

//तुमची declarations आणि definitions 


#endif

जेव्हा अशी हेडर फाईल include होईल तेव्हा प्रथम हे चेक केल्या जाईल कि SOME_UNIQUE_NAME_HERE  हे अगोदर कुणी define तर नाही ना केलं ? जर नसेल केलं तर आणि तरच #define SOME_UNIQUE_NAME_HERE आणि #endif च्या मधले content कॉपी केल्या जाईल. अगोदरच कुणीतरी include केले असेल तर पूर्ण हेडरफाईलच ignore केल्या जाईल. त्यामुळे डुप्लिकेट इम्पोर्ट होणारच नाही, म्हणून आपल्या प्रत्येक फाईल मध्ये हेडर गार्ड असायला पाहिजे. मग नाव काय द्यायचं ? काही भलतं सलत नाव देत बसण्यापेक्षा जे हेडर फाईल च नाव आहे तेच हेडर गार्ड च नाव कॅपिटल अक्षरात द्या जास्त शब्द असतील तर अंडरस्कोर वापरून जोडा आणि शेवटी _H लावा. उदा आपण आपली maths.h आणि geometry.h फाईल हेडर गार्ड वापरून तयार करूया. तर ती अशी दिसेल. 

१. math.h:
#ifndef MATHS_H
#define MATHS_H

int getSquareSides()
{
    return 4;
}
#endif  /*MATHS_H*/

२. geometry.h:
#ifndef GEOMETRY_H
#define GEOMETRY_H

#include "maths.h"

#endif  /*GEOMETRY_H*/

३. main.cpp:


#include "maths.h"    
#include "geometry.h" 

int main(void){
   return 0;
}

standard library सुद्धा हेडर गार्ड वापरतात जर तुम्ही iostream हेडर फाईल ला Visual Studio मध्ये बघाल तर खालील प्रमाणे दिसेल. 




हेडर गार्ड एक हेडर फाईल हि  वेग वेगळ्या source फाईल मध्ये इम्पोर्ट होणार नाही याला काही हि प्रतिबंध करू शकत नाही. 
गार्ड केलेली हेडर फाईल हि एका source फाईल मध्ये एक पेक्षा जास्त वेळ इम्पोर्ट होणार नाही याची खात्री हेडर गार्ड देते. हेडर गार्ड च designच तस असल्यामुळे ते एक आणि एकच source फाईल वर होईल परंतु दुसऱ्याही source फाईल न include केलेली असल्यास्त ती हेडर फाईल त्या source फाईल मध्ये सुद्धा include होईल. असं झाल्यास आपण अजून एका कचाट्यात सापडू .उदा :
१.square.h:
#ifndef SQUARE_H
#define SQUARE_H

int getSquareSides()
{
    return 4;
}

int getSquarePerimeter (int getSide);

#endif  /*SQUARE_H*/

२. square.cpp:
#include "square.h"    // इथे एकदा include झाली 

int getSquarePerimeter(int getSide){
   return getSide* getSquareSides();
}

३. main.cpp:
#include < iostream >
#include "square.h"   // इथे पुन्हा एकदा include झाली 

int main(void){
   std::cout << "a square has " << getSquareSides() << " sides" << std::endl;
   std::cout << "a square of length 5 has perimeter length " 
             << getSquarePerimeter(5) <<std::endl;
   return 0;
}

इथे square.h फाईल मध्ये जरी हेडर गार्ड असलं तरी त्यातले कन्टेन्ट एकदा square.cpp आणि एकदा main.cpp मध्ये कॉपी झाल. हे असं का झालं ते जरा खोलात जाऊन बघूया. 

जेव्हा square.h हेडर फाईल square.cpp फाईल मधून include झाली तेव्हा square.cpp फाईल चा स्कोप संपे पर्यंतच SQUARE_H defined राहील. त्यामुळे square.h फाईल हि फक्त square.cpp source फाईल मध्ये परत include होणार नाही. जसा square.cpp फाईल संपेल SQUARE_H सुद्धा not defined होईल , म्हणजेच आपण असं म्हणू शकतो कि जेव्हा प्रिप्रोसेसर main.cpp मध्ये रन होईल तेव्हा SQUARE_H हा main.cpp मध्ये सुरवातीला defined नाही होत. 

शेवटी getSquareSides() नावाचं function हे दोन्ही ठिकाणी येईल म्हणजेच square.cpp आणि main.cpp सुद्धा . प्रोग्रॅम तर compile होईल परंतु लिंकर ओरडेल कि getSquareSides() function जास्त वेळ आलं आहे म्हणून. 

हा अडथळा पार पडायला बरेच मार्ग आहे पण त्यातला सोपा म्हणजे हेडर फाईल मध्ये फक्त declaration ठेवा आणि कोणत्याही एका cpp फाईल मध्ये definition/implementation लिहा. 

१.square.h:
#ifndef SQUARE_H
#define SQUARE_H

int getSquareSides();

int getSquarePerimeter (int getSide);

#endif  /*SQUARE_H*/

२. square.cpp:
#include "square.h"    // इथे एकदा include झाली आणि इथे च इम्पलेमेंटेशन केलं 
int getSquareSides()
{
    return 4;
}

int getSquarePerimeter(int getSide){ return getSide* getSquareSides(); }

३. main.cpp:
#include < iostream >
#include "square.h"   // इथे पुन्हा एकदा include झालीतरी प्रॉब्लेम नाही येणार कारण डेफिनिशन एकच आहे  

int main(void){
   std::cout << "a square has " << getSquareSides() << " sides" << std::endl;
   std::cout << "a square of length 5 has perimeter length " 
             << getSquarePerimeter(5) <<std::endl;
   return 0;
}

आता जरी हेडर फाईल दोनदा अली असली तरी डेफिनिशन एकच असल्यामुळे लिंकर आनंदी आहे त्यामुळे आपला अडथळा मिटला . 

#pragma once
बरीच कंपायलर हेडर गार्ड च्या ऐवजी/ किंवा अल्टरनेटीव्ह म्हणून #pragma  directive सपोर्ट करतात.

#pragma once






#pragma once हेडर गार्ड सारखीच काम करतात , फरक एकच कि इथे एकाच लाईन मध्ये काम होऊन जाते. 

iostream मध्ये सुद्धा याचा वापर केलेला आहे आधीच्या image मध्ये बघा. सांगायचं झालं तर #pragma once हे  C++ कडून official नाही आहे. आणि सर्वच कंपायलर सपोर्ट करतील असते नाही (आजची बरीच सपोर्ट करतात वरील image मध्ये दाखवल्याप्रमाणे ) जुने प्रोग्रॅम सुद्धा चालावी म्हणून ( backward compatibility) असावी म्हणून आपण हेडर गार्ड चा वापर करावा 


पुनरावलोकन : 


Duplicate definition टाळण्यासाठी हेडर गार्ड तयार करण्यात आले परंतु हेडर गार्ड फक्त एकाच फाईल मध्ये एखादी हेडर फाईल एक पेक्षा जास्त वेळ येणार नाही याची खात्री देते , दुसऱ्या source फाईल मध्ये येणार नाही याची नाही . 

Thursday, 27 April 2017

Basic of Header files in marathi

हेडर फाईल्स बद्दल बोलू काही तरी ... 

जसा जसा प्रोग्रॅम मोठा मोठा होत जातो आणि आपणही लागल्या तशा फाईल्स include करत जातो, तसा तसा आपल्याला प्रत्येक प्रोग्रॅम फाईल मध्ये forward declaration / signature लिहीत बसावं लागते आणि हे प्रत्येक फाईल मध्ये जाऊन शोधत बसा अति जिवावर येण्यासारखं काम आहे , त्यामुळे असं काही करता येणार नाही का कि सगळी forward declarations / signatures हि एकाच फाईल मध्ये कोंबता येईल आणि तीच फाईल सगळीकडे वापरता येईल ? तीच फाईल म्हणजे हेडर फाईल .h एक्स्टेंशन असलेली फाईल, यालाच काहीजण include फाईल म्हणतात. अशा काही फाईल्स च एक्स्टेंशन .hpp नि सुद्धा असतात , तर मग लगेच प्रश्न पडतो .h आणि .hpp फाईल्स मध्ये काय फरक ?? बघूया  पुढे. 
बाकीच्या source फाईल्स ला लागणाऱ्या declarations लिहून ठेवणं हाच हेडर फाईल्स चा उद्देश आहे. 

standard library मधल्या header files :

खालील प्रोग्रॅम बघा :

#include <iostream>
int main()
{
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

हा प्रोग्रॅम "Hello, world!" console वर प्रिंट करेल. बघितलं तर या प्रोग्रॅम मध्ये आपण cout नावाचं काहीहि डिफाइन केलेलं नाही आहे , तर मग कंपायलर ला कस कळल कि cout काय आहे ते ?  याच उत्तर cout हे "iostream" नावाच्या हेडर फाईल मध्ये declared केलेलं आहे. जेव्हा आपण #include <iostream> असं लिहू तेव्हा आपण कंपायलर ला सांगत असतो iostream नावाच्या हेडर फाईल मध्ये असलेले सगळी कन्टेन्ट इन्कलुडींग फाईल मध्ये टाक. त्यामुळेच आपल्या source फाईल मध्ये हेडर फाईल मध्ये असलेली सगळी declared fun आपण आरामशीर वापरू शकतो. एक गोष्ट नेहमी लक्षात ठेवा कि "हेडर फाईल्स मध्ये फक्त declarations असतात" मग लगेच प्रश्न पडतो कि त्याचा actual इम्पलेमेंटेड कोड कुठे आहे , तो इम्पलेमेंटेड कोड  C वा C++ runtime support library मध्ये असतो. हाच कोड लिंकर लिंक करताना आपल्या कोड मध्ये चिपकवतो. त्यामुळेच कंपायलर नि तयार केलेल्या .obj फाईल चा size बघा आणि लिंकर नि लिंक केल्यावरचा साईझ बघा , फरक तुम्हालाच जाणवेल. 

समजा iostream हेडर फाईल नसती तर तुम्ही काय केलं असत ? जेव्हा जेव्हा std::cout तुम्ही वापरल तेव्हा तेव्हा तुम्ही स्वतः coutचा कोड manually प्रत्येक फाईल वर टाकत बसला असता, हो असं केल्यावर तुमचा कोड आरामशीर चालला असता, आणि असच पाहिजे तेच पाहिजेल तेव्हाच पाहिजे तोच कोड जर आपण लिहिला तर केव्हाही उत्तम . प्रोग्रॅम खूपच मजबूत होईल म्हणजे पाहिजे नसलेला कचरा आपल्या executable सोबत लिंक होऊन येणार नाही. हे करण्यासाठी काय घ्यायचं काय नाही याच ज्ञान असं अत्यावश्यक आहे. तरीही सुरवात करणार्यांनी सरळ हेडर फाईल लावा, यश घ्या आणि मग हे असले प्रयोग करत बसा .. उचापती ... :P 

असो आपण स्वतःची हेडर फाईल कशी लिहायची ते बघूया :

समजा आपल्याकडे २ source फाईल्स आहेत  

add.cpp 

int add(int num1, int num2){

   return num1+ num2;

}


main.cpp

#include <iostream>

int add(int num1, int num2);

int main(){

std::cout << "sum of two number is :"<< add(10,20) << std::endl; 

return 0;
}

इथे  main.cpp compile होताना कंपायलर ला add काय आहे हे कळण्यासाठी आपण एक forward declaration वापरलं . जस आधी म्हटल्याप्रमाणे प्रत्येक function साठी प्रत्येक फाईल मध्ये forward declaration लिहीत बसणं आणि तेच function जर दुसऱ्या source फाईल मध्ये वापरायचे असल्यास त्या फाईल च्या वर सुद्धा forward declaration टाकत बसणं लवकरच किचकट होत जाते. 

हेडर फाईल हे काम खूप सोप्प करते. हेडर फाईल मध्ये एकदा forward declaration लिहा आणि पाहिजे तितक्या source फाईल मध्ये वापरा तेही एका ओळीत #include <फाईल च नाव >
याचा अजून एक चांगला फायदा आहे समजा function चा prototype काही कारणास्तव बदला लागला म्हणजे एखादा parameter टाका लागला किंवा आहे तोच बदला लागला तर हेडर मध्ये बदल केला कि तो सगळीकडे रिफ्लेक्ट होईल. त्यामुळे प्रत्येक फाईल मध्ये जाऊन बदलत बसण्याचे कष्ट वाचेल :P (वाचलेल्या वेळात गोट्या खेळा :P). 

स्वतःची हेडर फाईल तयार करणं खूपच सोपं आहे, हेडर फाईल दोन भागात बघितली तर पहिला भाग असतो तो म्हणजे header guard, या बद्दल सविस्तर बघूया header guard वर बोलूया काहीतरी मध्ये.. थोडक्यात सांगायचं झालं तर हेडर गार्ड हे हेडर फाईल एका source फाईल मध्ये एकापेक्षा जास्त वेळ #इम्पोर्ट होणार नाही याची काळजी घेते. 

दुसरा भाग म्हणजे हेडर फाईल चा actual content ज्या मध्ये आपण सगळ्या functions साठीची सगळी declarations लिहू जेणेकरून सगळ्या source फाईल ला दिसेल अर्थातच कंपायलर ला . 
चला आपण आपली add.h फाईल तयार करू 

add.h 
// पहिल्या काही ओली header guard ची सुरवात करतात. ADD_H या नावाच्या जागी कोणताही unique name 
// चाललं असत परंतु convention प्रमाणे आपण हेडर फाईलचाच नाव टाकूया.
#ifndef ADD_H
#define ADD_H

// इथून actual content म्हणजेच declarations टाकायचे. 
int add(int x, int y); // function prototype for add.h -- इथे सेमी कोलन टाकायला विसरू नका

// header guard चा शेवट
#endif


आता हि फाईल आपल्या main.cpp मध्ये वापराची असेल तर इम्पोर्ट करावी लागेल 

main.cpp
#include <iostream>
#include "add.h"
int main(){ std::cout << "sum of two number is :"<< add(10,20) << std::endl; return 0; }


आणि add.cpp आहे तशीच राहील 
int add(int num1, int num2){

   return num1+ num2;

}

जेव्हा आपण main.cpp compile करू तेव्हा preprocesor #include "add.h" या लाईन ला add.h मधला कन्टेन्ट त्याच लाईन च्या जागी कॉपी करेल त्यामुळे add मेथड च proptotype as a forward declaration आपल्याला उपलब्ध होईल. आपला प्रोग्रॅम सरळ सरळ compile आणि link होईल. 
टीप : जेव्हा आपण एखादी फाईल  #include करतो , इम्पोर्ट करते वेळी  त्या  फाईल मधला सगळा कन्टेन्ट source फाईल मध्ये कॉपी केल्या जातो


जर तुम्हाला कंपायलर error आली कि add.h not being found तर परत तपासून पहा फाईल नाव आणि इम्पोर्ट मध्ये लिहिलेले नाव व्यवस्थित आहे कि नाही. नसल्यास कंपायलर बोंबलणारच. save करताना कदाचित  “add” (no extension) किंवा  “add.h.txt” किंवा  “add.hpp” असं save झालं असेल परत नीट तपासून घ्या.
आणि जर लिंकर बोंबलत असेल कि add() not being defined मग add.cpp आपल्या प्रोजेक्ट मध्ये बरोबर include झाली का नाही ते तपासून पहा. 

Angled brackets vs quotes
आता तुम्हाला नक्की पेच पडला असणार iostream साठी angled brackets का वापरले आणि add.h साठी double quotes का वापरले ? अँगल ब्रॅकेट कंपायलर ला सांगत असतो कि हि फाईल सिस्टिम डिरेक्टरी मधून घे म्हणजेच C++ च्या स्टॅंडर्ड लायब्ररी जिथे आहे तिथे. आणि डबल quote सांगत कि हि फाईल जिथे source फाईल आहे त्याच डिरेक्टरी मध्ये किंवा user नि स्पेसिफाइड केलेल्या path वर मिळेल. 
नियम : अँगल ब्रॅकेट तेव्हा वापरायचे जेव्हा तुम्हाला कंपायलर सोबत आलेल्या हेडर फाईल इंपोर्ट करायच्या आणि डबल ब्रॅकेट तेव्हा वापरायचे जेव्हा तुम्हाला तुमची फाईल  किंवा दुसरी एखादी  फाईल इम्पोर्ट करायच्या असतील तेव्हा. 
एक हेडर फाईल बाकी हेडर फाईल ला इम्पोर्ट करू शकतात परंतु आपण त्यावर अवलंबून नाही राहायचं , प्रत्येक वेळी ज्या ज्या लागतील त्या त्या फाईल्स आपण स्वतः इम्पोर्ट करायच्या, कारण असेही होऊ शकते या version यामध्ये एका फाईल नि स्वतःच इम्पोर्ट केली होती आणि दुसऱ्या मध्ये गायब झाली म्हणून अवलंबून राहण्यापेक्षा स्वतः केलेल बर. 
नियम : प्रत्येक .cpp फाईल मध्ये ज्या ज्या  कंपायलर ला लागतात त्या त्या हेडर फाईल #include करायच्या.
iostream ला .h extension का नाही आहे ?
हा प्रश्न सुरवातीला c झाल रे झालं आणि cpp ला सुरवात केली रे केली कि पहिलीच ओळ #include < iostream > बस इथेच सूरु होते 
आयुष्य तेच आहे 
अन हाच पेच आहे  - संगीता जोशी पुणे  यांच्या एका गझलेचा मतला :)
हि ओळ पहिली हा आपली सर्वांची पंचाईत होते. पहिलाच प्रश्न iostream ला .h extension गेलं कुठे ? 
याच उत्तर सोप आहे पण सुरवातीला माहिती नसते कि iostream आणि iostream.h या दोन्ही वेग वेगळ्या फाईल्स आहेत. हे समजून घेण्यासाठी थोडा भूतकाळ आठवावा लागतो. हे खरेच आहे इतिहास माहित नसला तर काही खरं नाही...!
जेव्हा नुकताच C++ चा  जन्म झाला तेव्हा standard runtime library मधल्या सर्व फाईल्स .h होत्या. हे सर्व सुरळीत सुरु होत आणि चांगल पण होत. सुरवातीला cout आणि cin हे iostream.h मधेच होते परंतु कालांतराने जेव्हा  ANSI committee नि C++ च standardization केल तेव्हा त्यांनी runtime library मध्ये असलेली सर्व functions उचलून std namespace मध्ये टाकली (आणि ते चांगलच होत). जरी इथे चांगल्यासाठी केलं असलं तरी मोठा पेच पडला. जर सगळी च्या सगळी functions std namespace मध्ये टाकल्या गेली तर जुने प्रोग्रॅम चालतील कसे त्यांना cout cin सारखी अति महत्वाची functions दिसणारच नाही आणि उलट कोणताही जुना प्रोग्रॅम चालणार नाही. म्हणून त्यांनी जुनी लायब्ररी फाईल्स तशीच ठेवली #include <iostream.h> आणि नवीन std namespace मध्ये टाकलेल्या फाईल्स तयार केल्या त्याला एक्स्टेंशन नाही दिल्या गेलं. म्हणून आज आपण इम्पोर्ट करताना #include <iostream> असच करतो. 
आज प्रोग्रॅम करताना हे लक्षात ठेवा कि जर स्टॅंडर्ड लायब्ररी मध्ये बिना एक्स्टेंशन वाली हेडर फाईल असल्यास बिनधास्त ती प्राधान्याने वापरा. असून सुद्धा जर तुम्ही .h वाली फाईल वापरत असाल तर मात्र तुम्ही deprecate झालेली functionality वापरत आहात (NOT OUTDATED , OLD THINGS NEVER GET OUTDATED) deprecate होणे म्हणजे त्याला आता कुठलाही सपोर्ट नाही परंतु ते अजूनही जून होत तसेच आहे किंवा राहील ,ते यापुढे upgrade होणार नाहीत.  
बघायला गेलं तर अजूनही बऱ्याच लायब्ररी ज्या C पासून inherit झाल्यात आणि आजही त्या आपण खूप वापरतो, C++ यामध्ये अशा लायब्ररी c prefix पासून सुरु होतात उदा  stdlib.h तयार झाली ती cstdlib मध्ये. naming collisions टाळण्यासाठी यातली सुद्धा functions std namespace मध्ये टाकल्या गेली. परंतु आपण आपल्या हेडर फाईल तयार करताना .h extension देऊन तयार करणार आहोत कारण आपण आपला कोड std namespace मध्ये टाकणार नाही आहोत. 

नियम : .h एक्स्टेंशन असलेली लायब्ररी उपलब्ध असल्यास तीच वापरा , std namespace चा वापर करून त्यातली functions वापरा . .h version नाही आहे किंवा स्वतःची  headers तयार करणार असाल तर .h वापरा 
दुसऱ्या directories मधून फाईल्स Include करताना :  
आता दुसरा पेच .. other directories मधून हेडर फाईल कशा इम्पोर्ट करायच्या ??
एक मार्ग (वाईट) आहे  त्या फाईल चा रेलॅटिव्ह path #include line मध्ये टाकणे, उदा :
#include <iostream>
#include "headers/add.h"
#include "../abc/headers/sub.h"
या पद्धतीने जर आपण वापर केला तर आपण डिरेक्टरी structure च्या बंधनात अडकून राहतो परत डिरेक्टरी structure मध्ये बदल करता येत नाही आणि केला तर लगेच कंपायलर बोंबलतो हेडर फाईल सापडत नाही आहे म्हणून हि पद्धत लहान प्रोजेक्ट पूरती मर्यादित वापरता येईल. 
यावरचा सोपा उपाय म्हणजे जर कमांड लाईन वापरून compile करत असाल तर अतिशय उत्तम कारण आपण कंपायलर ला आधीच सांगू शकतो तुला ह्या ह्या डिरेक्टरी मध्ये सुद्धा शोध घ्यायचा आहे. तसेच IDE असेल तर त्यात इकडे जा तिकडे जा असं शोधत compiler आणि linker या दोन्ही ठिकाणी आपल्याला हेडर फाईल जिथे आहे तो path स्वतः सांगा लागतो. 
उदा : 
Visual Studio असेल तर Solution Explorer मध्ये जा मग तिथे प्रोजेक्ट वर right क्लिक करा मग प्रॉपर्टी वर क्लिक करा मग “VC++ Directories” tab वर जा आणि तिथे “Include Directories” मध्ये आपली डिरेक्टरी टाका जिथे आपण हेडर फाईल्स ठेवल्या आहेत. 
Code::Blocks असेल तर प्रोजेक्ट मेनू मध्ये जा मग “Build Options” सिलेक्ट करा मग  “Search directories” tab वर जा मग इथे आपली डिरेक्टरी टाका जिथे आपण हेडर फाईल्स ठेवल्या आहेत. 
कमांड लाईन : एका ओळीत काम फक्त -I option द्यायचा 
उदा :
Visual Studio Developer Command Prompt 
cl.exe /EHsc main.cpp  /I "/source/includes"
g++ 
g++ -o main -I /source/includes main.cpp
या पद्धतीचा फायदा हाच कि तुम्ही डिरेक्टरी स्ट्रक्टर बदलू शकता , बदल केला तरी प्रत्येक फाईल मध्ये बदल न करता फक्त कमांड लाईन ला path बदल करायचा किंवा IDE setting बदलायच्या. 

हेडर फाईल मध्ये  function definitions ठेवू शकतो का ?
C किंवा C++ बोंबलणार तर नाही परंतु खरं सांगायचं तर नका ठेवू .  का ?
जस मी वर सांगितलं कि हेडर फाईल चा कन्टेन्ट सगळा जसा च्या तसा  #include लाईन ला रिप्लेस केल्या जातो कॉपी केल्या जातो . म्हणजेच आपण एक जरी function हेडर मध्ये लिहिलं आणि ती हेडर फाईल जर का खूप cpp फाईल मध्ये इम्पोर्ट असेल तर मात्र बोंबच बोंब होईल. 
प्रोजेक्ट लहान असेल तर काही प्रश्न नाही परंतु मोठा असल्यास फायदा कमी अन तोटेच जास्त अशी गत होईल . म्हणजे काय कंपायलर अजून जास्त वेळ घेईल कंपायलेशन साठी. कोड ची साईझ वाढेल उलट executable ची साईझ बिनकामीच डबल ट्रिपल झाली असेल. जर आपण फक्त कोड फाईल मध्ये बदल केला तर तीच कोड फाईल आपल्याला compile करा लागेल परंतु जर एका हेडर फाईल मध्ये बदल केला आणि ती फाईल हजार कोड फाईल मध्ये include असेल तर मात्र हजार फाईलस सुद्द्धा सगळ्या compile झाल्याचं पाहिजे . एक लहान बदल सुद्धा हजार राडे करायला भाग पाडतो. म्हणून कधीही हेडर फाईल मध्ये function definitions ठेवू नका. फक्त declaration ठेवा . 


Header file लिहिण्याचे best practices काय ?
स्वतःची हेडर फाईल तयार करताना खालील मुद्दे ध्यानात असू द्या :
  • नेहमी हेडर गार्ड टाकत जा 
  • variables कधीही हेडर फाईल मध्ये टाकू नका , constants असेल तर काही प्रश्न नाही , हेडर फाईल कधीही फक्त declarations साठीच वापरावी 
  • function  कधीही हेडर फाईल मध्ये ठेवू नका. 
  • प्रत्येक हेडर फाईल त्या त्या कामासाठीच ठेवा म्हणजे समजा add करणारी सगळी functions add.h मध्ये ठेवा , वजाबाकी करणारी वजाबाकी.h  मध्ये ठेवा. 
  • हेडर फाईल ला नाव देताना शक्यतो ज्या क्लास फाईल साठी बनवली तेच नाव द्या जेणेकरून नंतर गोंधळ होणार नाही . 
  •  #include करताना ज्या लागतात त्याच  #include करा. 
  •  .cpp फाईल ला कधीही  #include करू नका . 



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

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