#Pragma एक बार स्वचालित रूप से मान क्यों नहीं लिया जाता है?


81

फ़ाइल को केवल एक बार शामिल करने के लिए संकलक को विशेष रूप से बताने का क्या मतलब है? क्या यह डिफ़ॉल्ट रूप से समझ में नहीं आएगा? क्या एक भी फाइल को कई बार शामिल करने का कोई कारण है? सिर्फ मान ही क्यों नहीं लेते? क्या यह विशिष्ट हार्डवेयर के साथ करना है?


24
क्या एक भी फाइल को कई बार शामिल करने का कोई कारण है? => हो सकता है। किसी फ़ाइल #ifdefsमें इसमें सशर्त संकलन हो सकता है । तो तुम कह सकते हैं #define MODE_ONE 1और फिर #include "has-modes.h", और फिर #undef MODE_ONEसाथ #define MODE_TWO 1और #include "has-modes.h"फिर से। प्रीप्रोसेसर इस तरह की चीजों के बारे में अज्ञेय है, और शायद कभी-कभी वे समझ में आ सकते हैं।
HostileFork का कहना है कि

66
यह डिफ़ॉल्ट के रूप में समझ में आता है। बस वे नहीं जिन्हें उन्होंने उठाया था जब सी प्रोग्रामर अभी भी घोड़ों की सवारी करते थे, बंदूक चलाते थे और 16KB मेमोरी रखते थे
हंस पैसेंट

11
आप एक ही स्रोत फ़ाइल में, <assert.h>विभिन्न परिभाषाओं के साथ कई बार शामिल कर सकते हैं NDEBUG
पीट बेकर

3
के रूप में #pragma onceही है, वहाँ हार्डवेयर वातावरण जहां यह सही काम नहीं करेगा (आमतौर पर नेटवर्क ड्राइव और एक ही हेडर के लिए संभव अनेक पथों के साथ) कर रहे हैं।
पीट बेकर

12
यदि आपने #pragma onceमान लिया है, तो उस डिफ़ॉल्ट को काउंटर करने का तरीका क्या है? #pragma many? कितने कंपाइलरों ने ऐसा कुछ भी लागू किया है?
जोनाथन लेफ़लर

जवाबों:


85

यहाँ कई संबंधित प्रश्न हैं:

  • #pragma onceस्वचालित रूप से लागू क्यों नहीं किया जाता है?
    क्योंकि ऐसी परिस्थितियां हैं जिनमें आप एक से अधिक बार फाइलें शामिल करना चाहते हैं।

  • आप एक फ़ाइल को कई बार क्यों शामिल करना चाहेंगे?
    अन्य उत्तरों (Boost.Preprocessor, X-Macros सहित, डेटा फ़ाइलों सहित) में कई कारण दिए गए हैं। मैं "कोड डुप्लिकेट से बचें" का एक विशेष उदाहरण जोड़ना चाहूंगा : OpenFOAM एक शैली को प्रोत्साहित करता है जहां #includeफ़ंक्शन के भीतर बिट्स और टुकड़े एक आम अवधारणा है। उदाहरण के लिए इस चर्चा को देखें ।

  • ठीक है, लेकिन यह ऑप्ट-आउट के साथ डिफ़ॉल्ट क्यों नहीं है?
    क्योंकि यह वास्तव में मानक द्वारा निर्दिष्ट नहीं है। #pragmas परिभाषा कार्यान्वयन-विशिष्ट एक्सटेंशन द्वारा हैं।

  • #pragma onceएक मानकीकृत सुविधा क्यों नहीं बन गई है (क्योंकि यह व्यापक रूप से समर्थित है)?
    क्योंकि प्लेटफ़ॉर्म-अज्ञेयवादी तरीके से "वही फ़ाइल" क्या है, यह वास्तव में आश्चर्यजनक रूप से कठिन है। अधिक जानकारी के लिए यह उत्तर देखें



4
विशेष रूप से, इस उदाहरण को ऐसे मामले के लिए देखें जहां pragma onceविफल रहता है, लेकिन इसमें शामिल गार्ड ने काम किया होगा। स्थान के आधार पर फ़ाइलों की पहचान करना या तो काम नहीं करता है क्योंकि कभी-कभी एक ही परियोजना में कई बार एक ही फ़ाइल होती है (जैसे आपके पास 2 सबमॉड्यूल हैं जो दोनों हेडर में केवल एक हेडर-लाइब्रेरी शामिल करते हैं और इसकी स्वयं की प्रतिलिपि देखें)
MM

6
सभी व्यावहारिक कार्यान्वयन-विशिष्ट एक्सटेंशन नहीं हैं। जैसे #pragma STDCपरिवार । लेकिन वे सभी कार्यान्वयन-परिभाषित व्यवहार को नियंत्रित करते हैं।
रुस्लान

4
@ user4581301 यह जवाब एक बार प्रज्ञा के साथ समस्या को बढ़ा देता है और इसमें गार्ड शामिल होने के कारण परेशानियों पर विचार नहीं करता है। दोनों ही मामलों में कुछ अनुशासन की जरूरत होती है। गार्ड्स को शामिल करने के साथ एक नाम का उपयोग करना सुनिश्चित करना चाहिए जो किसी अन्य फ़ाइल में उपयोग नहीं किया जाएगा (जो एक फ़ाइल कॉपी संशोधित होने के बाद होगा)। एक बार प्रज्ञा के साथ, किसी को यह तय करना होगा कि उसकी फ़ाइल के लिए अद्वितीय सही जगह क्या है, जो कि एक अच्छी बात है।
ओलिव

3
@ मेहरदाद: क्या आप गंभीरता से सुझाव दे रहे हैं कि संकलक स्रोत फ़ाइलों को लिखें !? यदि कोई कंपाइलर देखता है #ifndef XX, तो उसे यह पता नहीं चल सकता है कि #endifजब तक वह पूरी फाइल नहीं पढ़ लेता है, तब तक उसके बाद भी क्या होता है । एक कंपाइलर जो इस बात पर नज़र रखता है कि बाहरी #ifndefफ़ाइल को पूरी तरह से एनक्लोज करता है और नोट करता है कि वह किस मैक्रो की जाँच करता है, वह फ़ाइल को पुन: आकार देने से बचने में सक्षम हो सकता है, लेकिन यह कहने का निर्देश कि वर्तमान बिंदु के बाद महत्व का कुछ भी नहीं प्रतीत होता है, संकलक से भरोसा करने से अच्छा लगेगा। ऐसी बातें याद रखें।
सुपरकैट

38

आप एक फ़ाइल में #include कहीं भी उपयोग कर सकते हैं , न केवल वैश्विक दायरे में - जैसे, एक फ़ंक्शन के अंदर (और यदि आवश्यक हो तो कई बार)। यकीन है, बदसूरत और अच्छी शैली नहीं है, लेकिन संभव है और कभी-कभी समझदार (आपके द्वारा शामिल फ़ाइल के आधार पर)। यदि #includeकभी केवल एक समय की बात थी तो वह काम नहीं करेगा। #includeबस सब के बाद पाठ प्रतिस्थापन (cut'n'paste) गूंगा करता है, और सब कुछ आप शामिल नहीं है एक हेडर फ़ाइल होना चाहिए। आप - उदाहरण के लिए - #includeएक फ़ाइल जिसमें स्वत: जनरेट किया गया डेटा होता है जिसमें आरंभिक डेटा को प्रारंभ करने के लिए कच्चा डेटा होता है std::vector। पसंद

std::vector<int> data = {
#include "my_generated_data.txt"
}

और "my_generated_data.txt" संकलन के दौरान निर्माण प्रणाली द्वारा उत्पन्न कुछ हो।

या हो सकता है कि मैं आलसी / मूर्ख / मूर्ख हूं और इसे एक फ़ाइल में डाल दिया ( बहुत ही विवादित उदाहरण):

const noexcept;

और फिर मैं करता हूँ

class foo {
    void f1()
    #include "stupid.file"
    int f2(int)
    #include "stupid.file"
};

एक और, थोड़ा कम वंचित, उदाहरण एक स्रोत फ़ाइल होगी जहां कई कार्यों के लिए एक नामस्थान में बड़ी मात्रा में उपयोग करने की आवश्यकता होती है, लेकिन आप using namespace foo;वैश्विक रूप से केवल यह कहना नहीं चाहते हैं कि बहुत सारे अन्य सामानों के साथ वैश्विक नामस्थान को विचलित कर देगा। तुम नहीं चाहते। तो आप एक फ़ाइल "फू" युक्त बनाएँ

using std::vector;
using std::array;
using std::rotate;
... You get the idea ...

और फिर आप अपने स्रोत फ़ाइल में ऐसा करते हैं

void f1() {
#include "foo" // needs "stuff"
}

void f2() {
    // Doesn't need "stuff"
}

void f3() {
#include "foo" // also needs "stuff"
}

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

यह भी हो सकता है कि जिस फ़ाइल को आप शामिल करते हैं, वह कुछ मैक्रोज़ ( #defineनों) के मूल्य के आधार पर भिन्न व्यवहार करती है । तो आप फ़ाइल को कई स्थानों में शामिल करना चाह सकते हैं, पहली बार कुछ मूल्य बदलने के बाद, इसलिए आपको अपनी स्रोत फ़ाइल के विभिन्न भागों में अलग-अलग व्यवहार मिलता है।


1
यह तब भी काम करेगा जब सभी हेडर एक बार प्रैग्मेंट हो गए। जब तक आप जनरेट किए गए डेटा को एक से अधिक बार शामिल नहीं करते।
PSkocik

2
@PSkocik लेकिन शायद मुझे इसे एक से अधिक बार शामिल करने की आवश्यकता है। मुझे क्यों नहीं होना चाहिए?
जेस्पर जुहल

2
@JesperJuhl यह बात है। आपको इसे एक से अधिक बार शामिल करने की आवश्यकता नहीं होगी । आपके पास वर्तमान में विकल्प है, लेकिन विकल्प बहुत बुरा नहीं है, अगर बिल्कुल भी।
जॉनी कैश

9
@PSkocik अगर मैं #defineप्रत्येक के पहले s का मान बदलता हूं जिसमें शामिल फ़ाइल का व्यवहार बदल जाता है, तो मुझे अपने स्रोत फ़ाइल के विभिन्न भागों में उन विभिन्न व्यवहारों को प्राप्त करने के लिए इसे कई बार शामिल करने की आवश्यकता हो सकती है।
जेसपर जुहल

27

एक्स-मैक्रो तकनीक के साथ कई बार उपयोग करने योग्य है जैसे :

data.inc:

X(ONE)
X(TWO)
X(THREE)

use_data_inc_twice.c

enum data_e { 
#define X(V) V,
   #include "data.inc"
#undef X
};
char const* data_e__strings[]={
#define X(V) [V]=#V,
   #include "data.inc"
#undef X
};

मैं किसी अन्य उपयोग के बारे में नहीं जानता।


यह बहुत जटिल लगता है। कोई कारण नहीं पहली जगह में फ़ाइल में उन परिभाषाओं को शामिल करने के लिए?
जॉनी कैश

2
@ जॉनीचैच: उदाहरण एक्स-मैक्रोज़ कैसे काम करता है इसका एक सरलीकृत संस्करण है। कृपया लिंक पढ़ें; वे सारणीबद्ध डेटा के जटिल जोड़-तोड़ करने के लिए कुछ मामलों में बेहद उपयोगी हैं। एक्स-मैक्रोज़ के किसी भी महत्वपूर्ण उपयोग में, ऐसा कोई तरीका नहीं होगा जिससे आप "फाइल में उन परिभाषाओं को शामिल कर सकें"।
निकोल बोलस

2
@ जॉनी - हाँ - एक अच्छा कारण है निरंतरता सुनिश्चित करना (जब आपके पास केवल कुछ दर्जन तत्व हों, तो कभी भी सैकड़ों का मन न हो)।
टोबी स्पाइट

@TobySpeight Heh, मुझे लगता है कि मैं हजारों लोगों को कहीं और लिखने से बचने के लिए कोड की एक पंक्ति को छोड़ सकता हूं। समझ में आता है।
जॉनी कैश

1
नकल से बचने के लिए। खासकर अगर फ़ाइल बड़ी है। बेशक, आप X मैक्रो सूची वाले एक बड़े मैक्रो का उपयोग कर सकते हैं, लेकिन चूंकि परियोजनाएं इस का उपयोग कर सकती हैं, इसलिए अनिवार्य #pragma onceव्यवहार एक ब्रेकिंग परिवर्तन होगा।
PSkocik

21

आप इस धारणा के तहत काम कर रहे हैं कि भाषा में मौजूद "#include" सुविधा का उद्देश्य कई संकलन इकाइयों में कार्यक्रमों के अपघटन के लिए समर्थन प्रदान करना है। यह गलत है।

यह उस भूमिका को निभा सकता है, लेकिन यह इसका उद्देश्य नहीं था। सी मूल रूप से विकसित किया गया था रूप से यूनिक्स को फिर से लागू करने के लिए पीडीपी -11 मैक्रो -11 विधानसभा की तुलना में थोड़ी उच्च-स्तरीय भाषा के रूप में किया । इसमें मैक्रो प्रीप्रोसेसर था क्योंकि यह मैक्रो -11 की एक विशेषता थी। इसमें मैक्रोज़ को किसी अन्य फ़ाइल से सम्मिलित करने की क्षमता थी क्योंकि यह मैक्रो -11 की एक विशेषता थी कि मौजूदा यूनिक्स वे अपने नए सी कंपाइलर में पोर्ट कर रहे थे।

अब यह पता चला है कि "#include" कोड को संकलन इकाइयों में अलग करने के लिए उपयोगी है , जैसा कि (यकीनन) हैक का एक सा है। हालाँकि, इस हैक के मौजूद होने का मतलब यह था कि यह C में किया गया रास्ता बन गया। तथ्य यह है कि जिस तरह से अस्तित्व में था उसका मतलब कोई नया तरीका नहीं था इस कार्यक्षमता को प्रदान करने के लिए आवश्यकता नहीं है, इसलिए कुछ भी सुरक्षित नहीं है (उदाहरण: एकाधिक के लिए असुरक्षित नहीं -inclusion) कभी बनाया गया था। चूंकि यह पहले से ही सी में था, इसलिए इसे सी के बाकी सिंटैक्स और मुहावरों के साथ-साथ सी ++ में कॉपी किया गया।

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


5
हमेशा की तरह, C और C ++ को समझने के लिए, आपको उनके इतिहास को समझने की आवश्यकता है।
जैक एदले

यह है उम्मीद करना उचित है कि मॉड्यूल फरवरी में भूमि जाएगा।
डेविस हेरिंग

7
@DavisHerring - हाँ, लेकिन फरवरी कौन सी है?
TED

10

नहीं, यह उदाहरण के लिए, पुस्तकालय लेखकों के लिए उपलब्ध विकल्पों में महत्वपूर्ण बाधा होगी। उदाहरण के लिए, Boost.Preprocessor किसी को प्री-प्रोसेसर लूप का उपयोग करने की अनुमति देता है, और उन्हें प्राप्त करने का एकमात्र तरीका एक ही फ़ाइल के कई समावेशन है।

और Boost.Preprocessor कई बहुत उपयोगी पुस्तकालयों के लिए एक बिल्डिंग ब्लॉक है।


1
इससे उसमे कोई बाधा नहीं आएगी । ओपी ने एक डिफ़ॉल्ट व्यवहार के बारे में पूछा , एक अपरिवर्तनीय व्यवहार नहीं। यह पूरी तरह से डिफ़ॉल्ट को बदलने और #pragma reentrantइन लाइनों के साथ एक प्रीप्रोसेसर झंडा या कुछ और प्रदान करने के लिए समझदार होगा । हेंडसाइट 20/20 है।
कोनराड रुडोल्फ

यह लोगों को अपने पुस्तकालयों और निर्भरता को अपडेट करने के लिए मजबूर करने के अर्थ में बाधा बनेगा, @KonradRudolph। हमेशा एक समस्या नहीं है, लेकिन यह कुछ विरासत कार्यक्रमों के साथ मुद्दों का कारण बन सकता है। आदर्श रूप में, वहाँ भी निर्दिष्ट करने के लिए एक कमांड लाइन स्विच डिफ़ॉल्ट है कि क्या होगा onceया reentrant, इस या अन्य संभावित मुद्दों को कम करने।
जस्टिन टाइम -

1
@JustinTime मेरी टिप्पणी के अनुसार, यह स्पष्ट रूप से एक पीछे संगत (और इसलिए संभव) परिवर्तन नहीं है। हालांकि, सवाल यह था कि इसे शुरू में इस तरह से डिजाइन किया गया था , न कि इसे क्यों नहीं बदला जा रहा है। और इसका जवाब, स्पष्ट रूप से है, कि मूल डिजाइन दूरगामी परिणामों के साथ एक बड़ी गलती थी।
कोनराड रूडोल्फ

8

जिस उत्पाद पर मैं मुख्य रूप से काम करता हूं, उसके लिए फर्मवेयर में, हमें यह निर्दिष्ट करने में सक्षम होना चाहिए कि फ़ंक्शंस और ग्लोबल / स्टैटिक वैरिएबल को मेमोरी में कहाँ आवंटित किया जाना चाहिए। वास्तविक समय प्रसंस्करण को चिप पर एल 1 मेमोरी में रहने की आवश्यकता होती है ताकि प्रोसेसर सीधे, तेजी से पहुंच सके। चिप पर कम महत्वपूर्ण प्रसंस्करण L2 मेमोरी में जा सकता है। और किसी भी चीज को विशेष रूप से शीघ्रता से संभालने की जरूरत नहीं है, बाहरी डीडीआर में रह सकते हैं और कैशिंग से गुजर सकते हैं, क्योंकि यह थोड़ा धीमा होने पर कोई फर्क नहीं पड़ता।

#Pragma को आवंटित करने के लिए जहां चीजें जाती हैं एक लंबी, गैर-तुच्छ रेखा है। इसे गलत निकालना आसान होगा। यह गलत होने का प्रभाव हो सकता है कि कोड / डेटा चुपचाप (डीडीआर) स्मृति डिफ़ॉल्ट में डाल दिया जाएगा, और के प्रभाव है कि बंद लूप नियंत्रण किया जा सकता है कोई कारण नहीं देखना आसान है कि के लिए काम रोक।

इसलिए मैं फ़ाइलों को शामिल करता हूं, जिसमें सिर्फ वह प्रज्ञा है। मेरा कोड अब इस तरह दिखता है।

शीर्ष लेख फ़ाइल...

#ifndef HEADERFILE_H
#define HEADERFILE_H

#include "set_fast_storage.h"

/* Declare variables */

#include "set_slow_storage.h"

/* Declare functions for initialisation on startup */

#include "set_fast_storage.h"

/* Declare functions for real-time processing */

#include "set_storage_default.h"

#endif

और स्रोत ...

#include "headerfile.h"

#include "set_fast_storage.h"

/* Define variables */

#include "set_slow_storage.h"

/* Define functions for initialisation on startup */

#include "set_fast_storage.h"

/* Define functions for real-time processing */

आप एक ही फ़ाइल के कई समावेशों को देखेंगे, यहाँ तक कि हेडर में भी। अगर मैं अब कुछ गलत करता हूं, तो कंपाइलर मुझे बताएगा कि इसमें शामिल फ़ाइल "set_fat_storage.h" नहीं मिल सकती है और मैं इसे आसानी से ठीक कर सकता हूं।

तो आपके प्रश्न के उत्तर में, यह एक वास्तविक, व्यावहारिक उदाहरण है जहां कई समावेश की आवश्यकता है।


3
मैं कहूंगा कि आपका उपयोग मामला _Pragmaनिर्देश के लिए एक प्रेरक उदाहरण है । वही प्रैग्मस अब नियमित मैक्रोज़ से विस्तारित किया जा सकता है। इसलिए एक से अधिक बार शामिल करने की आवश्यकता नहीं है।
स्टोरीटेलर - Unslander Monica
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.