2011-03-24 17 views
9

मेरे सी हेडर आमतौर पर निम्नलिखित शैली सदृश शामिल किए जाने के से बचने के लिए: अपने Notes on Programming in C मेंअच्छा सी हैडर शैली

#ifndef <FILENAME>_H 
#define <FILENAME>_H 

// define public data structures/prototypes, macros etc. 

#endif /* !<FILENAME>_H */ 

हालांकि,, रोब पाईक हेडर फाइल के बारे में निम्न तर्क बनाता है:

नहीं है #ifdef से जुड़ा एक छोटा सा नृत्य जो फ़ाइल को दो बार पढ़ने से रोक सकता है, लेकिन आमतौर पर यह अभ्यास में गलत होता है - #ifdef फाइल में ही है, न कि फ़ाइल जिसमें यह शामिल है। नतीजा प्रायः लिक्सिकल विश्लेषक के माध्यम से गुजरने वाली कोड की हजारों अनगिनत रेखाएं होती है, जो कि (अच्छे कंपाइलर्स में) सबसे महंगा चरण है।

एक तरफ, पाइक एकमात्र प्रोग्रामर है जिसे मैं वास्तव में प्रशंसा करता हूं। दूसरी तरफ, एक ही हेडर फ़ाइल में #ifdef डालने की बजाय कई स्रोत फ़ाइलों में कई #ifdef एस डालने की आवश्यकता अनावश्यक रूप से अजीब लगती है।

एकाधिक समावेशन की समस्या को संभालने का सबसे अच्छा तरीका क्या है?

उत्तर

11

मेरी राय में, उस विधि का उपयोग करें जिसके लिए आपके कम समय की आवश्यकता होती है (जिसका अर्थ है कि हेडर फाइलों में #ifdefs डालने का मतलब है)। अगर मेरा परिणामी कोड क्लीनर है तो संकलक को कड़ी मेहनत करनी पड़ेगी तो मुझे सच में बुरा नहीं लगता है। यदि, शायद, आप एक बहु-मिलियन लाइन कोड बेस पर काम कर रहे हैं जिसे आपको लगातार पुनर्निर्माण करना है, तो अतिरिक्त बचत इसके लायक है। लेकिन ज्यादातर मामलों में, मुझे संदेह है कि अतिरिक्त लागत आमतौर पर ध्यान देने योग्य नहीं है।

+2

मुझे यह जवाब बहुत उपयोगी लगता है। मजेदार बात यह है कि मैं एक और अधिक विडंबनात्मक तरीके से सहमत हूं: यदि कंप्यूटर मेरा काम कर सकता है, तो मैं ऐसा क्यों करूँगा? ;) –

6

जो भी आप करते हैं वह करते रहें - यह स्पष्ट, कम बग-प्रवण है, और संकलक लेखकों द्वारा अच्छी तरह से जाना जाता है, इसलिए यह एक दशक या दो साल पहले अक्षम नहीं था।

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

+0

यदि जीसीसी चूसना नहीं था, तो यह पहली बार हेडर को शामिल करने के लिए मानक मुहावरे का पता लगाएगा, फ़ाइल नाम याद रखेगा, और उस फ़ाइल नाम को शामिल करने के लिए सभी भावी अनुरोधों को अनदेखा करेगा। यह पूरी तरह से संकलक आलस्य का मामला है। –

+1

@ आर। यह अभी भी नहीं है? मैंने सोचा होगा कि हर दशक में हर प्रमुख कंपाइलर होगा। फिर फिर, फाइल को दो बार पढ़ना वास्तव में संसाधन हॉग नहीं है, जब यह पाइक ने उन नोट्स को लिखा था। – Erik

+1

@ आर ..: ऐसा करने का दावा है - http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html। हालांकि, कुछ प्रतिबंध हैं जो यह सुनिश्चित करने के लिए उपयोग करते हैं कि अनुकूलन मान्य है, और ऐसे प्रतिबंध हो सकते हैं जहां प्रतिबंध टूट गए हैं, लेकिन फिर भी अनुकूलन उस मामले में मान्य होगा। –

2

जिस तरह से आप वर्तमान में इसे कर रहे हैं वह आम तरीका है। पाइक की विधि संकलन समय पर थोड़ा सा कटौती करती है, लेकिन आधुनिक कंपाइलरों के साथ शायद बहुत अधिक नहीं (जब पाइक ने अपने नोट लिखे थे, तो कंपाइलर ऑप्टिमाइज़र-बाध्य नहीं थे), यह मॉड्यूल और इसके बग-प्रोन को अव्यवस्थित करता है।

आप अभी भी हेडर से हेडर समेत बहु-समावेश में कटौती कर सकते हैं, लेकिन इसके बजाय उन्हें इस शीर्षलेख को शामिल करने से पहले <foodefs.h> शामिल करें। "

1

मुझे सलाह है कि आप उन्हें स्रोत-फ़ाइल में ही रखें। वास्तविक पीसी के साथ कोड की कुछ हज़ार जरूरत रहित पार्स लाइनों के बारे में शिकायत करने की आवश्यकता नहीं है।

इसके अतिरिक्त - यदि आप प्रत्येक स्रोत-फ़ाइल में प्रत्येक शीर्षलेख की जांच करते हैं तो यह अधिक काम और स्रोत है जिसमें हेडर शामिल है।

और आपको अपने हेडर-फाइलों को डिफ़ॉल्ट से अलग करना होगा- और अन्य तृतीय-पक्ष-शीर्षलेख।

1

वह उस समय तर्क दे सकता था जब वह इसे लिख रहा था। आजकल सभ्य कंपाइलर्स इस अच्छी तरह से संभालने के लिए पर्याप्त चालाक हैं।

+0

संदर्भ? मुझे लगता है कि वे पर्याप्त चालाक नहीं हैं ... –

+1

@R।, एचएम, संदर्भ, नहीं मेरे पास नहीं है। मैंने पढ़ा है कि यहां बहुत समय पहले एसओ पर नहीं है, जिसमें गार्ड शामिल हैं जीसीसी द्वारा '#pragma एक बार' के बराबर माना जाता है। और बीटीडब्लू '# ifdef' के लेक्सिकल पार्सिंग को पहली बार देखने के लिए कि फाइल के लिए वास्तविक रूप से पार्स किया जाना कितना मुश्किल नहीं होना चाहिए, नहीं? किसी भी मामले में, मैंने कभी मेरे लिए नहीं पाया कि * यह * बड़े कोड को संकलित करते समय एक बाधा थी। प्री-प्रोसेसिंग चरण क्या होता है यह देखने के लिए मैं अक्सर '-E' के साथ प्रीकंपिलेशन करता हूं और यह मानव आंखों द्वारा मापने योग्य नहीं है। –

0

मैं आपके दृष्टिकोण से सहमत हूं - जैसा कि अन्य ने टिप्पणी की है, इसकी स्पष्ट, स्वयं-दस्तावेज, और कम रखरखाव।

मेरा सिद्धांत क्यों रॉब पाइक ने अपना दृष्टिकोण सुझाया होगा: वह सी के बारे में बात कर रहा है, सी ++ नहीं।

सी ++ में, यदि आपके पास बहुत सारी कक्षाएं हैं और आप प्रत्येक को अपनी शीर्ष लेख फ़ाइल में घोषित कर रहे हैं, तो आपके पास बहुत सारी शीर्षलेख फ़ाइलें होंगी। सी वास्तव में इस प्रकार की अच्छी तरह से तैयार संरचना प्रदान नहीं करता है (मुझे कई सिंगल-स्ट्रक्चर सी हेडर फाइलों को याद नहीं है), और .h/.c फ़ाइल-जोड़े बड़े होने के लिए और मॉड्यूल की तरह कुछ शामिल है या उपप्रणाली। तो, कम हेडर फाइलें। उस परिदृश्य में रोब पाइक का दृष्टिकोण काम कर सकता है। लेकिन मैं इसे गैर-तुच्छ सी ++ कार्यक्रमों के लिए उपयुक्त नहीं देखता हूं।

3

पाईक https://talks.golang.org/2012/splash.article में इसके बारे में कुछ और लिखा है:

1984 में, ps.c का एक संकलन, यूनिक्स ps कमांड के स्रोत, #include <sys/stat.h> 37 बार करने के लिए समय सभी preprocessing था द्वारा मनाया गया किया गया। हालांकि सामग्री को 36 बार छोड़ दिया जाता है, जबकि अधिकांश सी कार्यान्वयन फ़ाइल खोलेंगे, इसे पढ़ें, और इसे सभी 37 बार स्कैन करें। महान चतुरता के बिना, वास्तव में, सी प्रीप्रोसेसर के संभावित जटिल मैक्रो अर्थशास्त्र द्वारा व्यवहार की आवश्यकता होती है।

कंपाइलर काफी चालाक हो गए हैं: https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html, इसलिए यह अब एक मुद्दा से कम है।

Google पर एक एकल सी ++ बाइनरी का निर्माण हजारों बार सैकड़ों व्यक्तिगत शीर्षलेख फ़ाइलों को खोल और पढ़ सकता है। 2007 में, Google के निर्माण इंजीनियरों ने प्रमुख Google बाइनरी के संकलन का वाद्य यंत्र किया। फ़ाइल में लगभग दो हजार फाइलें हैं, यदि बस एक साथ संयोजित, कुल 4.2 मेगाबाइट्स। उस समय # समावेशन का विस्तार किया गया था, संकलक के इनपुट में 8 गीगाबाइट वितरित किए जा रहे थे, प्रत्येक सी ++ स्रोत बाइट के लिए 2000 बाइट्स का झटका लगा।

2003 में एक और डेटा बिंदु के रूप में, Google की बिल्ड सिस्टम को एकल मेकफ़ाइल से प्रति-निर्देशिका डिज़ाइन में बेहतर-प्रबंधित, स्पष्ट निर्भरताओं के साथ स्थानांतरित किया गया था। एक सामान्य द्विआधारी फ़ाइल आकार में लगभग 40%, केवल रिकॉर्ड की गई अधिक सटीक निर्भरताओं से कम हो गई। इसके बावजूद, सी ++ (या उस मामले के लिए सी) के गुण स्वचालित रूप से उन निर्भरताओं को सत्यापित करने के लिए अव्यवहारिक बनाते हैं, और आज हमारे पास अभी भी बड़ी Google सी ++ बाइनरी की निर्भरता आवश्यकताओं की सटीक समझ नहीं है।

बाइनरी आकारों के बारे में बिंदु अभी भी प्रासंगिक है। कंपाइलर्स (लिंकर्स) अप्रयुक्त प्रतीकों को अलग करने के संबंध में काफी रूढ़िवादी हैं। How to remove unused C/C++ symbols with GCC and ld?

योजना 9 में, हेडर फाइल आगे #include खंड युक्त करने से मना कर रहे थे; सभी #includes को शीर्ष-स्तरीय सी फ़ाइल में होना आवश्यक था। इसके लिए कुछ अनुशासन की आवश्यकता थी, निश्चित रूप से प्रोग्रामर सही क्रम में आवश्यक निर्भरताओं को सूचीबद्ध करने के लिए आवश्यक था-लेकिन दस्तावेज़ीकरण में मदद मिली और प्रैक्टिस में यह बहुत अच्छी तरह से काम करता था।

यह एक संभावित समाधान है।एक और संभावना है कि एक उपकरण है जो आपके लिए शामिल करता है, उदाहरण के लिए MakeDeps

एकता निर्माण भी होता है, जिसे कभी-कभी एससीयू कहा जाता है, एकल संकलन इकाई बनाता है। इसे प्रबंधित करने में सहायता के लिए उपकरण हैं, जैसे https://github.com/sakra/cotire

वृद्धिशील संकलन की गति के लिए अनुकूलित करने वाली एक बिल्ड सिस्टम का उपयोग करना भी फायदेमंद हो सकता है। मैं Google के बैज़ल और इसी तरह के बारे में बात कर रहा हूं। यह आपको हेडर फ़ाइल में बदलाव से सुरक्षित नहीं करता है, हालांकि बड़ी संख्या में अन्य फ़ाइलों में शामिल है।

अंत में, कार्यों में सी ++ मॉड्यूल के लिए एक प्रस्ताव है, महान सामग्री https://groups.google.com/a/isocpp.org/forum/#!forum/modules। यह भी देखें What exactly are C++ modules?

+0

जैसा कि आपके दूसरे उद्धरण में पहले पैराग्राफ में देखा गया है, 2007 के अंत में समस्या अभी भी प्रमुख है। यदि केवल हाल ही के मानक उपलब्ध हैं। – FRIdSUN

+0

https://web.archive.org/web/20021218032155/http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html के अनुसार, जीसीसी पहले से ही 2002 के अंत में एकाधिक-समावेश अनुकूलन कर सकता है। मुझे आश्चर्य है कि 2007 में किस कंपाइलर का उपयोग कर Google का उपयोग कर रहा था। – user7610

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