क्या C++ 17, C++ 14, और C++ 11 ऑब्जेक्ट लिंक करना सुरक्षित है




c++11 linker (2)

इन वस्तुओं में से कौन सा संयोजन है और क्या यह एक एकल बाइनरी में लिंक करना सुरक्षित नहीं है? क्यूं कर?

GCC के लिए ऑब्जेक्ट A, B, और C. के किसी भी संयोजन को एक साथ जोड़ना सुरक्षित है। यदि वे सभी एक ही संस्करण के साथ बनाए गए हैं, तो वे ABI संगत हैं, मानक संस्करण (यानी -std विकल्प) से कोई फर्क नहीं पड़ता है ।

क्यूं कर? क्योंकि यह हमारे कार्यान्वयन की एक महत्वपूर्ण संपत्ति है जिसे हम सुनिश्चित करने के लिए कड़ी मेहनत करते हैं।

जहां आपको समस्याएं हैं यदि आप एक साथ लिंक करते हैं तो GCC के विभिन्न संस्करणों के साथ संकलित वस्तुएं और उस मानक के पूरा होने से पहले GCC के समर्थन से पहले आपने नए C ++ मानक से अस्थिर सुविधाओं का उपयोग किया है। उदाहरण के लिए, यदि आप GCC 4.9 और -std=c++11 का उपयोग करके किसी ऑब्जेक्ट को संकलित करते हैं और GCC 5 और -std=c++11 साथ कोई अन्य ऑब्जेक्ट आपको समस्याएँ होंगी। CCC 11 समर्थन GCC 4.x में प्रयोगात्मक था, और इसलिए GCC 4.9 और C ++ 11 सुविधाओं के 5 संस्करणों के बीच असंगत परिवर्तन हुए थे। इसी तरह, यदि आप GCC 7 और -std=c++17 साथ एक ऑब्जेक्ट संकलित करते हैं और GCC 8 और -std=c++17 साथ कोई अन्य ऑब्जेक्ट आपको समस्याएँ हैं, क्योंकि GCC 7 और 8 में C ++ 17 समर्थन अभी भी है प्रयोगात्मक और विकसित।

दूसरी ओर, निम्नलिखित वस्तुओं का कोई भी संयोजन काम करेगा (हालाँकि libstdc++.so बारे में नीचे ध्यान दें libstdc++.so संस्करण):

  • GCC 4.9 और -std=c++03 साथ संकलित ऑब्जेक्ट डी
  • ऑब्जेक्ट E को GCC 5 और -std=c++11 साथ संकलित किया गया है
  • ऑब्जेक्ट F को GCC 7 और -std=c++17 साथ संकलित किया गया है

ऐसा इसलिए है क्योंकि प्रयुक्त सभी तीन संकलक संस्करणों में C ++ 03 समर्थन स्थिर है, और इसलिए C ++ 03 घटक सभी वस्तुओं के बीच संगत हैं। GCC 5 के बाद से C ++ 11 समर्थन स्थिर है, लेकिन ऑब्जेक्ट D किसी भी C ++ 11 सुविधाओं का उपयोग नहीं करता है, और ऑब्जेक्ट E और F दोनों उन संस्करणों का उपयोग करते हैं जहां C ++ 11 समर्थन स्थिर है। C ++ 17 समर्थन किसी भी संकलित संकलक संस्करणों में स्थिर नहीं है, लेकिन केवल ऑब्जेक्ट F C ++ 17 सुविधाओं का उपयोग करता है और इसलिए अन्य दो ऑब्जेक्ट्स के साथ कोई संगतता समस्या नहीं है (केवल वे सुविधाएँ जो C ++ 03 से आती हैं। या C ++ 11, और उपयोग किए गए संस्करण उन भागों को ठीक बनाते हैं)। यदि आप बाद में GCC 8 और -std=c++17 का उपयोग करके एक चौथी वस्तु, G को संकलित करना चाहते हैं, तो आपको F को उसी संस्करण के साथ F को फिर से जोड़ने की आवश्यकता होगी (या F से लिंक नहीं) क्योंकि C ++ 17 प्रतीक F में और जी असंगत हैं।

डी, ई और एफ के बीच ऊपर वर्णित संगतता के लिए एकमात्र चेतावनी यह है कि आपके प्रोग्राम को libstdc++.so उपयोग करना चाहिए libstdc++.so जीसीसी 7 (या बाद में) से साझा लाइब्रेरी। क्योंकि ऑब्जेक्ट F को GCC 7 के साथ संकलित किया गया था, इसलिए आपको उस रिलीज़ से साझा लाइब्रेरी का उपयोग करने की आवश्यकता है, क्योंकि GCC 7 के साथ कार्यक्रम के किसी भी हिस्से को संकलित करना प्रतीकों पर निर्भरता का परिचय दे सकता है जो libstdc++.so में मौजूद नहीं हैं libstdc++.so G67 4.9 और GCC 5 से। इसी प्रकार, यदि आप GCC 8 के साथ निर्मित वस्तु G से जुड़े हैं, तो आपको libstdc++.so का उपयोग करने की आवश्यकता होगी libstdc++.so GCC 8 से G के लिए आवश्यक सभी प्रतीक सुनिश्चित करने के लिए। सरल नियम यह है कि साझा लाइब्रेरी को रन-टाइम पर उपयोग करने वाले प्रोग्राम को कम से कम नया सुनिश्चित करना है क्योंकि संस्करण किसी भी ऑब्जेक्ट को संकलित करने के लिए उपयोग किया जाता है।

जीसीसी का उपयोग करते समय एक और चेतावनी, आपके प्रश्न पर टिप्पणियों में पहले से ही उल्लेख किया गया है, कि जीसीसी 5 के बाद से std::string दो कार्यान्वयन libstdc ++ में उपलब्ध हैं। दो कार्यान्वयन लिंक-संगत नहीं हैं (उनके अलग-अलग नाम हैं, इसलिए उन्हें एक साथ जोड़ा नहीं जा सकता) लेकिन एक ही बाइनरी में सह-अस्तित्व हो सकता है (उनके अलग-अलग नाम हैं, इसलिए यदि कोई ऑब्जेक्ट std::string का उपयोग करता है तो संघर्ष न करें std::string और अन्य का उपयोग करता है std::__cxx11::string )। यदि आपकी वस्तुएं std::string उपयोग करती हैं तो आमतौर पर उन सभी को समान स्ट्रिंग कार्यान्वयन के साथ संकलित किया जाना चाहिए। मूल -D_GLIBCXX_USE_CXX11_ABI=1 gcc4-compatible कार्यान्वयन का चयन करने के लिए -D_GLIBCXX_USE_CXX11_ABI=0 साथ संकलित करें, या -D_GLIBCXX_USE_CXX11_ABI=1 नए cxx11 कार्यान्वयन का चयन करें (नाम से मूर्ख न cxx11 , इसे C ++ 03 में भी उपयोग किया जा सकता है, इसे cxx11 भी कहा जाता है cxx11 क्योंकि यह C ++ 11 आवश्यकताओं के अनुरूप है)। कौन सा कार्यान्वयन डिफ़ॉल्ट है यह निर्भर करता है कि जीसीसी को कैसे कॉन्फ़िगर किया गया था, लेकिन डिफ़ॉल्ट हमेशा मैक्रो के साथ संकलन-समय पर ओवरराइड किया जा सकता है।

मान लीजिए कि मेरे पास तीन संकलित वस्तुएं हैं, जो सभी एक ही संकलक / संस्करण द्वारा निर्मित हैं:

  1. A को C ++ 11 मानक के साथ संकलित किया गया था
  2. B को C ++ 14 मानक के साथ संकलित किया गया था
  3. C को C ++ 17 मानक के साथ संकलित किया गया था

सादगी के लिए, मान लें कि सभी हेडर C ++ 11 में लिखे गए थे, केवल उन निर्माणों का उपयोग करते हुए जिनके शब्दार्थ सभी तीन मानक संस्करणों के बीच नहीं बदले हैं , और इसलिए किसी भी अन्योन्याश्रयता को हेडर समावेशन के साथ सही ढंग से व्यक्त किया गया था और संकलक ने कोई आपत्ति नहीं की थी।

इन वस्तुओं में से कौन सा संयोजन है और क्या यह एक एकल बाइनरी में लिंक करना सुरक्षित नहीं है? क्यूं कर?

संपादित करें: प्रमुख संकलक (जैसे gcc, clang, बनाम ++) को कवर करने वाले उत्तर का स्वागत है


उत्तर के दो भाग हैं। कंपाइलर स्तर पर संगतता और लिंकर स्तर पर संगतता। पूर्व से शुरू करते हैं।

मान लीजिए कि सभी हेडर C ++ 11 में लिखे गए थे

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

कहा कि, यदि अनुवाद इकाई के संकलन के लिए प्रयुक्त संकलक विकल्प किसी विशेष C ++ मानक को निर्दिष्ट करते हैं, तो जो भी सुविधाएँ केवल नए मानकों में उपलब्ध हैं, वे सुलभ नहीं होनी चाहिए। यह __cplusplus निर्देश का उपयोग करके किया जाता है। यह कैसे उपयोग किया जाता है के एक दिलचस्प उदाहरण के लिए vector स्रोत फ़ाइल देखें। इसी तरह, संकलक मानक के नए संस्करणों द्वारा प्रस्तुत किसी भी वाक्यात्मक सुविधाओं को अस्वीकार कर देगा।

उस सभी का मतलब है कि आपकी धारणा केवल आपके द्वारा लिखी गई हेडर फाइलों पर लागू हो सकती है। ये हेडर फाइलें विभिन्न C ++ मानकों को लक्षित करने वाली विभिन्न अनुवाद इकाइयों में शामिल होने पर असंगति पैदा कर सकती हैं। यह सी ++ मानक के अनुलग्नक सी में चर्चा की गई है। 4 खंड हैं, मैं केवल पहले एक पर चर्चा करूंगा, और बाकी का संक्षेप में उल्लेख करूंगा।

C.3.1 क्लॉज 2: लेक्सिकल कन्वेंशन

एकल उद्धरण C ++ 11 में एक वर्ण शाब्दिक परिसीमन करते हैं, जबकि वे C ++ 14 और C ++ 17 में अंक विभाजक हैं। मान लें कि आपके पास शुद्ध C ++ 11 हेडर फ़ाइलों में से एक में स्थूल परिभाषा है:

#define M(x, ...) __VA_ARGS__

// Maybe defined as a field in a template or a type.
int x[2] = { M(1'2,3'4) };

शीर्ष लेख फ़ाइल में दो अनुवाद इकाइयों पर विचार करें, लेकिन क्रमशः C ++ 11 और C ++ 14 को लक्षित करें। C ++ 11 को लक्षित करते समय, उद्धरणों के भीतर अल्पविराम को पैरामीटर विभाजक नहीं माना जाता है; केवल एक बार पैरामीटर है। इसलिए, कोड इसके बराबर होगा:

int x[2] = { 0 }; // C++11

दूसरी ओर, C ++ 14 को लक्षित करते समय, एकल उद्धरणों को अंक विभाजकों के रूप में व्याख्या की जाती है। इसलिए, कोड इसके बराबर होगा:

int x[2] = { 34, 0 }; // C++14 and C++17

यहाँ मुद्दा यह है कि शुद्ध C ++ 11 हेडर फ़ाइलों में से किसी एक में एकल उद्धरणों का उपयोग करने से अनुवाद इकाइयों में आश्चर्यजनक कीड़े हो सकते हैं जो C ++ 14/17 को लक्षित करते हैं। इसलिए, भले ही हेडर फ़ाइल C ++ 11 में लिखी गई हो, यह सुनिश्चित करने के लिए सावधानीपूर्वक लिखा जाना चाहिए कि यह मानक के बाद के संस्करणों के साथ संगत है। __cplusplus निर्देश यहां उपयोगी हो सकता है।

मानक से अन्य तीन खंडों में शामिल हैं:

C.3.2 खण्ड 3: मूल अवधारणाएं

परिवर्तन : नया सामान्य (गैर-प्लेसमेंट) डीलर

तर्क : आकार के निपटारे के लिए आवश्यक है।

मूल सुविधा पर प्रभाव : मान्य C ++ 2011 कोड एक वैश्विक प्लेसमेंट आवंटन फ़ंक्शन और डीलक्लोलेशन फ़ंक्शन की घोषणा कर सकता है:

void operator new(std::size_t, std::size_t); 
void operator delete(void*, std::size_t) noexcept;

इस अंतर्राष्ट्रीय मानक में, हालांकि, ऑपरेटर हटाने की घोषणा एक पूर्वनिर्धारित सामान्य (गैर-प्लेसमेंट) ऑपरेटर हटाने (3.7.4) से मेल खा सकती है। यदि ऐसा है, तो कार्यक्रम बीमार है, क्योंकि यह वर्ग के सदस्य आवंटन कार्यों और डीलरशिप कार्यों (5.3.4) के लिए था।

C.3.3 क्लाज 7: घोषणाएं

परिवर्तन : कॉन्स्टैक्स गैर-स्थैतिक सदस्य फ़ंक्शंस निहित रूप से कॉन्स्टेबल सदस्य फ़ंक्शन नहीं हैं।

Rationale : ऑब्जेक्ट को म्यूट करने के लिए constexpr सदस्य फ़ंक्शन को अनुमति देने के लिए आवश्यक है।

मूल सुविधा पर प्रभाव : मान्य C ++ 2011 कोड इस अंतर्राष्ट्रीय मानक में संकलित करने में विफल हो सकता है।

उदाहरण के लिए, निम्न कोड C ++ 2011 में मान्य है लेकिन इस अंतर्राष्ट्रीय मानक में अमान्य है क्योंकि यह एक ही सदस्य फ़ंक्शन को अलग-अलग रिटर्न के साथ दो बार घोषित करता है:

struct S {
constexpr const int &f();
int &f();
};

C.3.4 क्लाज 27: इनपुट / आउटपुट लाइब्रेरी

परिवर्तन : परिभाषित नहीं किया गया है।

औचित्य : प्राप्त का उपयोग खतरनाक माना जाता है।

मूल सुविधा पर प्रभाव : मान्य C ++ 2011 कोड जो फ़ंक्शन का उपयोग करता है वह इस अंतर्राष्ट्रीय मानक में संकलित करने में विफल हो सकता है।

C.4 14 और C ++ 17 के बीच संभावित असंगतताओं पर C.4 में चर्चा की गई है। चूंकि सभी गैर-मानक हेडर फाइलें C ++ 11 (जैसा कि प्रश्न में निर्दिष्ट है) में लिखी गई हैं, ये समस्याएँ नहीं होंगी, इसलिए मैं यहां उनका उल्लेख नहीं करूंगा।

अब मैं लिंकर स्तर पर संगतता पर चर्चा करूंगा। सामान्य तौर पर, असंगतियों के संभावित कारणों में निम्नलिखित शामिल हैं:

यदि परिणामी ऑब्जेक्ट फ़ाइल का प्रारूप लक्ष्य C ++ मानक पर निर्भर करता है, तो लिंकर को विभिन्न ऑब्जेक्ट फ़ाइलों को लिंक करने में सक्षम होना चाहिए। जीसीसी, एलएलवीएम और वीसी ++ में, यह सौभाग्य से मामला नहीं है। यही है, ऑब्जेक्ट फ़ाइलों का प्रारूप लक्ष्य मानक के समान है, हालांकि यह संकलक पर अत्यधिक निर्भर है। वास्तव में, जीसीसी, एलएलवीएम और वीसी ++ के लिंकर्स में से किसी को भी लक्ष्य सी ++ मानक के बारे में ज्ञान की आवश्यकता नहीं है। इसका मतलब यह भी है कि हम पहले से संकलित ऑब्जेक्ट फ़ाइलों को जोड़ सकते हैं (स्टेटिक रूप से रनटाइम को लिंक कर रहे हैं)।

यदि प्रोग्राम स्टार्टअप रुटीन (फ़ंक्शन जो main कहता है) विभिन्न सी ++ मानकों के लिए अलग है और विभिन्न रूटीन एक दूसरे के साथ संगत नहीं हैं, तो ऑब्जेक्ट फ़ाइलों को लिंक करना संभव नहीं होगा। जीसीसी, एलएलवीएम और वीसी ++ में, यह सौभाग्य से मामला नहीं है। इसके अलावा, main फ़ंक्शन (और उस पर लागू होने वाले प्रतिबंध, मानक की धारा 3.6 देखें) के हस्ताक्षर सभी सी ++ मानकों में समान हैं, इसलिए यह कोई फर्क नहीं पड़ता कि यह किस अनुवाद इकाई में मौजूद है।

सामान्य तौर पर, WPO विभिन्न C ++ मानकों का उपयोग करके संकलित ऑब्जेक्ट फ़ाइलों के साथ अच्छी तरह से काम नहीं कर सकता है। यह इस बात पर निर्भर करता है कि संकलक के किन चरणों के लिए लक्ष्य मानक के ज्ञान की आवश्यकता होती है और कौन सी अवस्थाएँ नहीं होती हैं और इसका प्रभाव उस अंतर-प्रक्रियात्मक अनुकूलन पर पड़ता है जो ऑब्जेक्ट फ़ाइलों को पार करता है। सौभाग्य से, जीसीसी, एलएलवीएम, और वीसी ++ अच्छी तरह से डिज़ाइन किए गए हैं और इस मुद्दे पर नहीं हैं (ऐसा नहीं है कि मुझे पता है)।

इसलिए, CCC मानक के विभिन्न संस्करणों में बाइनरी संगतता को सक्षम करने के लिए GCC, LLVM, और VC ++ को डिज़ाइन किया गया है। यह वास्तव में मानक स्वयं की आवश्यकता नहीं है, हालांकि।

वैसे, हालांकि VC ++ कंपाइलर std स्विच प्रदान करता है, जो आपको C ++ मानक के किसी विशेष संस्करण को लक्षित करने में सक्षम बनाता है, यह C ++ 11 को लक्षित करने का समर्थन नहीं करता है। निर्दिष्ट किया जा सकता न्यूनतम संस्करण C ++ 14 है, जो Visual C ++ 2013 अपडेट 3 से शुरू होने वाला डिफ़ॉल्ट है। आप C ++ 11 को लक्षित करने के लिए VC ++ के पुराने संस्करण का उपयोग कर सकते हैं, लेकिन तब आपको विभिन्न VC ++ कंपाइलरों का उपयोग करना होगा विभिन्न अनुवाद इकाइयों को संकलित करने के लिए जो C ++ मानक के विभिन्न संस्करणों को लक्षित करते हैं, जो बहुत कम से कम डब्ल्यूपीओ को तोड़ते हैं।

गुफा: मेरा उत्तर पूर्ण या बहुत सटीक नहीं हो सकता है।





abi