2015-03-17 4 views
6

मैं वर्तमान में Arduino के लिए एक C++ लाइब्रेरी अपडेट कर रहा हूं (विशेष रूप से avr-gcc का उपयोग करके संकलित 8-बिट एवीआर प्रोसेसर)।बाहरी चर केवल हेडर में अप्रत्याशित रूप से काम कर रहा है, क्यों?

आम तौर पर डिफ़ॉल्ट Arduino पुस्तकालयों के लेखकों को हेडर के अंदर कक्षा के लिए एक बाहरी चर शामिल करना पसंद है, जिसे कक्षा .cpp फ़ाइल में भी परिभाषित किया गया है। यह मुझे लगता है कि अंतर्निहित वस्तुओं के रूप में नए लोगों के लिए जाने के लिए तैयार सबकुछ तैयार करना है।

मेरे पास परिदृश्य है: लाइब्रेरी जो मैंने अद्यतन किया है अब .cpp फ़ाइल की आवश्यकता नहीं है और मैंने इसे लाइब्रेरी से हटा दिया है। यह तब तक नहीं था जब तक कि मुझे एहसास हुआ कि उन बगों के लिए अंतिम पास की जांच नहीं हुई, इस तथ्य के बावजूद कि कोई लिंकर त्रुटि उत्पन्न नहीं हुई थी, एक 0cचर के लिए .cpp फ़ाइल में एक परिभाषा प्रदान नहीं की गई थी।

struct Foo{ 
    void method() {} 
}; 

extern Foo foo; 

इस कोड को शामिल करना और एक या कई स्रोत फ़ाइलों में इसका उपयोग करने के लिए किसी भी लिंकर त्रुटि का कारण नहीं है:

इस रूप में मैं इसे (हेडर फाइल) प्राप्त कर सकते हैं के रूप में सरल है। मैंने जीसीसी के दोनों संस्करणों में कोशिश की है जो Arduino (4.3.7, 4.8.1) और सी ++ 11 सक्षम/अक्षम के साथ उपयोग करता है।

त्रुटि के कारण मेरे प्रयास में, मैंने पाया कि ऑब्जेक्ट का पता लेने या डमी वैरिएबल की सामग्री को संशोधित करने जैसी कुछ करने पर ही यह संभव था।

इस खोज के बाद मैं नोट करने के लिए यह महत्वपूर्ण है लगता है:

  • दर्जे के प्रकार्य केवल, अन्य वस्तुओं लौटने के लिए, में के रूप में खुद के लिए संदर्भ लौटने ऑपरेटरों, या यहां तक ​​कि एक प्रति की तरह कुछ भी।
  • यह केवल बाहरी वस्तुओं को संशोधित करता है (रजिस्ट्रार जो प्रभावी रूप से volatile uint8_t कोड में संदर्भ हैं), और अन्य वर्गों के अस्थायी लौटाता है।
  • इस हेडर में सभी क्लास फ़ंक्शंस इतने बुनियादी हैं कि उन्हें फ़ंक्शन कॉल की लागत से कम या उसके बराबर खर्च होता है, इसलिए वे कॉलर में पूरी तरह से लाइन में हैं (मेरे परीक्षणों में)। एक सामान्य कथन कॉल श्रृंखला में कई अस्थायी वस्तुओं को बना सकता है, हालांकि कंपाइलर इनके माध्यम से देखता है और नेस्टेड फ़ंक्शन कॉल के सेट के बजाय सीधे रजिस्ट्रार को संशोधित करने वाले कुशल कोड आउटपुट करता है।

मैं भी n37977.1.1 में पढ़ने याद - 8 कि extern अधूरा प्रकार पर इस्तेमाल किया जा सकता है, तथापि वर्ग पूरी तरह से परिभाषित किया गया है, जबकि घोषणा नहीं है (यह शायद अप्रासंगिक है)।

मुझे विश्वास है कि यह खेल पर अनुकूलन का परिणाम हो सकता है। मैंने इस प्रभाव को देखा है कि पता लेना उन वस्तुओं पर है जो अन्यथा राम उपयोग के बिना स्थिर और संकलित माना जाएगा। एक ऑब्जेक्ट में संकेत की किसी भी परत को जोड़कर जिसमें संकलक राज्य की गारंटी नहीं दे सकता है, इस रैम उपभोग करने वाले व्यवहार का कारण बन जाएगा।

तो, शायद मैंने इसे पूछकर अपने प्रश्न का उत्तर दिया है, हालांकि मैं अभी भी धारणाएं कर रहा हूं और यह मुझे परेशान करता है। कुछ समय बाद शौक-कोडिंग सी ++, सचमुच की मेरी सूची में केवल एक चीज हैधारणा है।

वास्तव में, क्या मैं जानना चाहता हूँ है:

  • काम कर समाधान मेरे पास करने के लिए सम्मान के साथ, यह वर्ग के पते (कारण अविवेक) लेने के लिए अक्षमता का दस्तावेजीकरण का एक सरल मामला है?
  • क्या यह कुछ बढ़ने के लिए आवश्यकता को समाप्त करने वाले ऑप्टिमाइज़ेशन के कारण एक बढ़त मामला व्यवहार है?
  • या सादा और सरल अपरिभाषित व्यवहार है। जैसा कि जीसीसी में एक बग हो सकता है और कोड को अनुमति दे रहा है जो ऑप्टिमाइज़ेशन को कम या अक्षम कर दिया गया हो सकता है?

या आप में से एक भाग्यशाली अंगूठी के कब्जे में भाग्यशाली हो सकता है जो विशिष्ट रूपरेखा के मानक रूपरेखा में उपयुक्त पैराग्राफ पा सकता है।

यह मेरा पहला प्रश्न है, इसलिए मुझे बताएं कि क्या आप कुछ विवरण जानना चाहते हैं, तो यदि आवश्यक हो तो मैं कोड के गिटहब लिंक भी प्रदान कर सकता हूं।

संपादित करें: लाइब्रेरी को मौजूदा कोड के साथ संगत होने की आवश्यकता है, इसलिए मुझे डॉट सिंटैक्स का उपयोग करने की क्षमता बनाए रखने की आवश्यकता है, अन्यथा मेरे पास स्थिर कार्यों का एक वर्ग होगा।

अब के लिए मान्यताओं को निकालने के लिए मैं दो विकल्प देखेंगे:

  • बस चर घोषणा के लिए एक सीपीपी जोड़ें।
  • शीर्षलेख में परिभाषित करें जैसे कि #define foo (Foo()) अस्थायी के माध्यम से डॉट सिंटैक्स की अनुमति देता है।

मैं परिभाषित करने का तरीका पसंद करता हूं, समुदाय क्या सोचता है?

चीयर्स।

+0

सुसंगत रहें। बाहरी परिभाषाओं के लिए .cpp फ़ाइल जोड़ें और ठीक से टिप्पणी करें। परिभाषित करने से कोड को और खराब कर दिया जाएगा। – SChepurin

उत्तर

1

यह ऑप्टिमाइज़ेशन के कारण या तो एज केस व्यवहार है, या आप कभी भी अपने कोड में foo चर का उपयोग नहीं करते हैं। मुझे 100% यकीन नहीं है कि यह औपचारिक रूप से एक अनिर्धारित व्यवहार नहीं है, लेकिन मुझे पूरा यकीन है कि यह व्यावहारिक दृष्टिकोण से अपरिभाषित नहीं है।

extern वैरिएबल इस प्रकार लागू किए जाते हैं, उनके साथ संकलित कोड तथाकथित स्थानान्तरण उत्पन्न करता है - रिक्त स्थान जहां चर के जोड़ों को रखा जाना चाहिए - जो तब लिंकर द्वारा भरे जाते हैं। स्पष्ट रूप से foo का उपयोग कभी भी आपके कोड में इस तरह से नहीं किया जाता है कि इसे अपना पता प्राप्त करने की आवश्यकता होगी और इसलिए लिंकर उस प्रतीक को खोजने का प्रयास भी नहीं करता है। यदि आप ऑप्टिमाइज़ेशन बंद करते हैं (-O0) तो आपको शायद लिंकर त्रुटि मिल जाएगी।

अद्यतन: आप "डॉट नोटेशन" रखते हैं लेकिन अपरिभाषित निर्वासन के साथ समस्या को निकालना चाहते हैं, तो आप static साथ extern की जगह ले सकती (हेडर फाइल में), अलग प्रत्येक टीयू के लिए चर के "उदाहरण" का निर्माण। चूंकि इस चर को वैसे भी अनुकूलित किया जा रहा है, यह वास्तविक कोड को बिल्कुल भी नहीं बदलेगा, लेकिन यह अनियमित निर्माण के लिए भी काम करेगा।

+0

हां, '-O0' का उपयोग करके लिंकर त्रुटि हुई, जो कि मैंने अपनी पोस्ट में संदेह किया, अब पुष्टि हुई। हालांकि, परिभाषा की कमी नहीं होनी चाहिए, इस पर ध्यान दिए बिना कि संकलक कितना अच्छा काम कर सकता है। इसका व्यवहार उपयोग के बजाय अनुकूलन के आधार पर संदिग्ध हो जाता है। –

+1

@ क्रिसिस: वन डेफिनिशन नियम उल्लंघन के लिए कोई निदान की आवश्यकता नहीं है, क्योंकि इसके लिए लिंकर की आवश्यकता होगी, संकलक नहीं, इसका निदान करने के लिए पर्याप्त स्मार्ट होना चाहिए। यदि आप नियम तोड़ते हैं तो आपको अपरिभाषित व्यवहार मिलता है। –

+0

@ChrisA लाइब्रेरी लिखते समय वर्तमान व्यवहार बहुत ही विश्वसनीय हो सकता है: आप संपूर्ण लाइब्रेरी के लिए सामान्य शीर्षलेख बना सकते हैं, लेकिन संकलित कोड को कई फ़ाइलों में डाल सकते हैं, और केवल वास्तव में उपयोग किए गए लिंक को लिंक कर सकते हैं। – Frax

2

कुछ extern घोषित करने से केवल असेंबलर और लिंकर को सूचित किया जाता है कि जब भी आप उस लेबल/प्रतीक का उपयोग करते हैं, तो इसे स्थानीय रूप से आवंटित प्रतीक के बजाय प्रतीक तालिका में प्रविष्टि का संदर्भ लेना चाहिए।

लिंकर की भूमिका प्रतीक तालिका प्रविष्टियों को जब भी संभव हो, पता स्थान के वास्तविक संदर्भ के साथ प्रतिस्थापित करना है।

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

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