2013-02-14 23 views
6

अब, मुझे पता है कि गैर-पत्ते वर्गों में नए आभासी कार्यों को जोड़ने में आम तौर पर बुरा होता है क्योंकि यह किसी भी व्युत्पन्न कक्षाओं के लिए बाइनरी संगतता को तोड़ देता है जिसे पुन: संकलित नहीं किया गया है।शुद्ध वर्चुअल फ़ंक्शंस और बाइनरी संगतता

मैं उदाहरण के लिए, एक अंतरफलक वर्ग और कार्यान्वयन वर्ग किसी साझा लाइब्रेरी में संकलित किया है:: हालांकि, मैं एक अलग स्थिति है

class Interface { 
    public: 
     static Interface* giveMeImplPtr(); 
     ... 
     virtual void Foo(uint16_t arg) = 0; 
     ... 
} 

class Impl { 
    public: 
     ... 
     void Foo(uint16_t arg); 
     .... 
} 

मेरा मुख्य आवेदन इस शेयर की गई लाइब्रेरी का उपयोग करता है, और मूल रूप से लिखा जा सकता है के रूप में:

Interface* foo = Implementation::giveMeImplPtr(); 
foo->Foo(0xff); 

दूसरे शब्दों में, आवेदन किसी भी वर्ग है जो Interface से निकाले जाते हैं नहीं है, यह केवल इसे इस्तेमाल करता है।

अब, कहते हैं कि मैं Foo(uint32_t arg) साथ Foo(uint16_t arg) ओवरलोड करना चाहते हैं, मुझे क्या करना सुरक्षित हूँ:

class Interface { 
    public: 
     static Interface* giveMeImplPtr(); 
     ... 
     virtual void Foo(uint16_t arg) = 0; 
     virtual void Foo(uint32_t arg) = 0; 
     ... 
} 

और आवेदन पुनः संकलित किए बिना मेरी साझा लाइब्रेरी पुनः संकलित करें?

यदि हां, तो क्या कोई असामान्य चेतावनी है जिसके बारे में मुझे अवगत होना चाहिए? यदि नहीं, तो क्या मेरे पास हिट और अप-वर्जन लाइब्रेरी लेने के अलावा अन्य कोई विकल्प हैं, इस प्रकार पीछे की संगतता तोड़ रही है?

उत्तर

5

एबीआई मूल रूप से vtable सहित ऑब्जेक्ट के आकार और आकार पर निर्भर करता है। वर्चुअल फ़ंक्शन जोड़ना निश्चित रूप से vtable को बदल देगा, और यह कैसे बदलता है संकलक पर निर्भर करता है।

इस मामले में विचार करने के लिए कुछ और बात यह है कि आप केवल एबीआई तोड़ने का प्रस्ताव नहीं दे रहे हैं, लेकिन एक एपीआई संकलन समय पर पता लगाने में बहुत मुश्किल है। यदि इन आभासी कार्यों नहीं थे और ABI संगतता कोई मुद्दा नहीं था, कुछ अपने परिवर्तन के बाद की तरह:

void f(Interface * i) { 
    i->Foo(1) 
} 

चुपचाप अपने नए फ़ंक्शन को कॉल खत्म हो जाएगा, लेकिन केवल तभी है कि कोड कंपाइल किया जाता है, डिबगिंग कर सकते हैं जो बहुत कठिन।

5

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

2

जब मैं इसी स्थिति में था तो यह मेरे लिए बहुत ही अद्भुत था और मैंने पाया कि एमएसवीसी ओवरलोडेड कार्यों के क्रम को उलट देता है। अपने उदाहरण के अनुसार, MSVC v_table (बाइनरी में) इस तरह का निर्माण करेगी:

class Interface { 
    virtual void first() = 0; 
    virtual void Foo(uint16_t arg) = 0; 
    virtual void Foo(uint32_t arg) = 0; 
    virtual void Foo(std::string arg) = 0; 
    virtual void final() = 0; 
} 

MSVC निम्नलिखित v_table का निर्माण करेगी:

virtual void Foo(uint32_t arg) = 0; 
virtual void Foo(uint16_t arg) = 0; 

अगर हम इस तरह एक छोटे से अपने उदाहरण, चौड़ा करता हूँ :

virtual void first() = 0; 
    virtual void Foo(std::string arg) = 0; 
    virtual void Foo(uint32_t arg) = 0; 
    virtual void Foo(uint16_t arg) = 0; 
    virtual void final() = 0; 

Borland बिल्डर और जीसीसी आदेश को बदल नहीं है, लेकिन

  1. वे ऐसा नहीं इस में, यह एक महाकाव्य असफल

एक अंत हो सकता है कि संस्करणों, कि मैं

  • परीक्षण किया अपने पुस्तकालय जीसीसी (उदाहरण के लिए) द्वारा संकलित हैं, और एप्लिकेशन MSVC द्वारा संकलित किया जाएगा ... बाइनरी संगतता पर भरोसा न करें। वर्ग के किसी भी बदलाव से इसका उपयोग करके सभी कोड का पुनर्मूल्यांकन होना चाहिए।

  • 2

    आप लोकप्रिय वर्णन करने के लिए कोशिश कर रहे हैं "कक्षाएं बनाने गैर व्युत्पत्ति" सिम्बियन C++ API (देखने newl कारखाने विधि) में, दोहरी संगतता जो प्रयोग किया जाता है, उदाहरण के लिए संरक्षण के लिए तकनीक:

    1. फ़ैक्टरी फ़ंक्शन प्रदान करें;
    2. सी ++ निर्माता निजी (और गैर-निर्यात गैर इनलाइन, और कक्षा दोस्त वर्ग या कार्यों नहीं होना चाहिए) घोषित, इस वर्ग के गैर-व्युत्पत्ति बनाता है और फिर आप कर सकते हैं:

      • आभासी जोड़े कक्षा घोषणा के अंत में कार्य,
      • डेटा सदस्यों को जोड़ें और कक्षा के आकार को बदलें।

    इस तकनीक को केवल जीसीसी संकलक के लिए काम करता है क्योंकि यह द्विआधारी स्तर पर आभासी कार्यों के स्रोत आदेश बचाता है।

    स्पष्टीकरण

    आभासी काम करता है, घायल नाम से नहीं द्वारा एक वस्तु का v-तालिका में ऑफसेट लागू कर रहे हैं। यदि आप केवल स्थिर फैक्ट्री विधि को कॉल करके ऑब्जेक्ट पॉइंटर प्राप्त कर सकते हैं और सभी आभासी कार्यों के ऑफसेट को संरक्षित कर सकते हैं (स्रोत ऑर्डर को सहेजकर, अंत में नई विधियां जोड़कर) तो यह पिछड़ा बाइनरी संगत होगा।

    अगर अपनी कक्षा एक सार्वजनिक निर्माता (इनलाइन या गैर इनलाइन) है अनुकूलता ध्वस्त किया जा सकता:

    • इनलाइन: अनुप्रयोगों वर्ग के एक पुराने v-मेज और पुराने स्मृति लेआउट की प्रतिलिपि बनाएगा जो नई पुस्तकालय में इस्तेमाल किए गए लोगों से अलग होगा; यदि आप किसी भी निर्यात विधि को कॉल करते हैं या किसी ऑब्जेक्ट को ऐसी विधि के तर्क के रूप में पास करते हैं तो इससे सेगमेंटेशन गलती की स्मृति भ्रष्टाचार हो सकती है;

    • गैर इनलाइन: स्थिति, बेहतर है क्योंकि आप, पत्ती वर्ग घोषणा के अंत तक नया आभासी विधियां जोड़कर v-टेबल बदल सकते हैं क्योंकि लिंकर पर व्युत्पन्न वर्ग के वी-तालिका लेआउट पुनर्स्थापित हो जाएगा क्लाइंट साइड यदि आप नया लाइब्रेरी संस्करण लोड करेंगे; लेकिन आप अभी भी कक्षा के आकार को बदल नहीं सकते हैं (यानी नए फ़ील्ड जोड़ना), क्योंकि आकार संकलन समय पर हार्ड-कोड किया गया है और एक नया संस्करण कन्स्ट्रक्टर कॉल करने से क्लाइंट स्टैक या ढेर पर पड़ोसी वस्तुओं की स्मृति टूट सकती है।

    उपकरण

    कोशिश लिनक्स पर अपने वर्ग पुस्तकालय संस्करणों में से पिछड़े दोहरी संगतता की जाँच करने के abi-compliance-checker उपकरण का उपयोग करने के लिए।

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