2010-02-07 18 views
10

लिंक करते समय इनलाइन फ़ंक्शंस की एकाधिक परिभाषा मेरे पास एक सी ++ प्रोग्राम है जिसे मैं mingw (विंडोज़ के लिए जीसीसी) के साथ संकलित करता हूं। Mingw के टीडीएम रिलीज का उपयोग जिसमें जीसीसी 4.4.1 शामिल है। दो स्थैतिक पुस्तकालय (.a) फ़ाइलों के निष्पादन योग्य लिंक: उनमें से एक सी में लिखी एक तृतीय पक्ष पुस्तकालय है; दूसरा एक सी ++ लाइब्रेरी है, जो मेरे द्वारा लिखी गई है, जो सी लाइब्रेरी का उपयोग करती है, शीर्ष पर अपना स्वयं का सी ++ एपीआई प्रदान करती है।स्थैतिक libs

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

यह एकाधिक परिभाषा त्रुटियों का कारण नहीं बनता है जब फ़ाइलों को एक ही प्रोजेक्ट में अलग-अलग .c या .cpp फ़ाइलों में कई बार उपयोग किया जाता है; समस्या यह है कि यह पुस्तकालय प्रति एक परिभाषा उत्पन्न करता है।

दोनों पुस्तकालयों में इन इनलाइन फ़ंक्शंस के लिए कंपाइलर जनरेटिंग फ़ंक्शंस और प्रतीक कैसे/क्यों हैं? मैं इसे अपने कोड में उत्पन्न करना बंद करने के लिए कैसे मजबूर कर सकता हूं? क्या कोई उपकरण है जो मैं .a फ़ाइल से डुप्लिकेट फ़ंक्शंस को पट्टी करने के लिए चला सकता हूं, या लिंकर को कई परिभाषाओं को अनदेखा करने का एक तरीका है?

(एफवाईआई, तीसरी पार्टी लाइब्रेरी में #ifdef __cplusplus और बाहरी "सी" गार्ड अपने सभी शीर्षकों में शामिल हैं; वैसे भी अगर यह समस्या थी, तो यह प्रतीक की एक एकाधिक परिभाषा नहीं पैदा करेगा, इससे विपरीत समस्या होगी क्योंकि प्रतीक अपरिभाषित या कम से कम अलग होगा।)

विशेष रूप से, लिंक त्रुटियां तब नहीं होती हैं जब मैं तृतीय पक्ष सी लाइब्रेरी के DLL से लिंक करता हूं; हालांकि मुझे अजीब रनटाइम विफलताओं मिलती हैं जो कि मेरे कोड के साथ अपने कार्यों के संस्करण के साथ करना पड़ता है, इसे डीएलएल से कॉल करना चाहिए। (जैसे कि कंपाइलर फ़ंक्शंस के स्थानीय संस्करण बना रहा है, मैंने नहीं पूछा था।)

इस प्रश्न के समान संस्करणों से पहले पूछा गया है, हालांकि, मुझे इनमें से किसी भी स्थिति में मेरी स्थिति का उत्तर नहीं मिला:

इस सवाल का जवाब था कि पोस्टर गुणा चर को परिभाषित किया गया था, मेरी समस्या इनलाइन कार्यों के कई परिभाषा है: Repeated Multiple Definition Errors from including same header in multiple cpps

यह एक MSVC कार्यक्रम था, लेकिन मैं MinGW उपयोग कर रहा हूँ; भी, इस सवाल में पोस्टर की समस्या एक शीर्षक में वर्ग शरीर के बाहर वर्ग निर्माता एक सी की ++ परिभाषा था, जबकि मेरी समस्या सी कार्यों कि इनलाइन हैं के साथ है: Static Lib Multiple Definition Problem

इस मूर्ख सी के रूप में अपने सभी सी कोड नाम दिया ++ Multiple definition of lots of std:: functions when linking

यह एक सिर्फ जानना क्यों एक परिभाषा नियम का उल्लंघन करने में कोई त्रुटि नहीं था चाहता था: सुरक्षित - फ़ाइलें और उसकी सी कोड सी ++ नहीं था unpredictable behavior of Inline functions with different definitions

+1

सूचना है कि C99 इनलाइन अर्थ विज्ञान सी के ++ एक से अलग हैं नहीं: सी में , यदि इनलाइन फ़ंक्शन घोषणाओं में से एक स्पष्ट रूप से 'बाहरी' निर्दिष्ट किया गया है, तो यह बाहरी परिभाषा बनाता है - जो अब एक इनलाइन परिभाषा नहीं है। ये बाहरी परिभाषा कार्यक्रम में कई बार प्रकट नहीं हो सकती हैं। सी ++ में इस तरह के फ़ंक्शन पर एक स्पष्ट 'बाहरी' का कोई प्रभाव नहीं पड़ता है। सी –

+0

में 'स्थैतिक इनलाइन' करने के साथ आप सबसे अच्छे हैं। आम तौर पर इनलाइन फ़ंक्शन परिभाषाओं को 'कमजोर' प्रतीक के रूप में चिह्नित किया जाता है और लिंकर को सभी डुप्लिकेट को ही हटा दिया जाता है। – Omnifarious

+0

धन्यवाद जोहान्स: प्रीप्रोसेसर परिभाषाओं का उपयोग करते हुए, सी लाइब्रेरी की हेडर फाइलें इन कार्यों को "इनलाइन" घोषित करती हैं यदि सी लाइब्रेरी की .c फाइलों में और "बाहरी इनलाइन" जब वे मेरे प्रोजेक्ट में हों। लेकिन मुझे यकीन नहीं है कि उन्होंने ऐसा क्यों किया। सी लाइब्रेरी में कुछ कोड है जो एक फ़ंक्शन के पते को अद्वितीय कुंजी मान के रूप में उपयोग करता है। एक सी ++ प्रोग्रामर के रूप में मैं देखता हूं कि खराब अभ्यास के रूप में और मैं वास्तव में प्रार्थना करता हूं कि उन्होंने इस तरह के इनलाइन फ़ंक्शन के पते का उपयोग नहीं किया। मुझे लगता है कि इन टिप्पणियों के बीच उत्तर देने के लिए लगभग पर्याप्त जानकारी है, हालांकि मैं अभी भी प्रोग्राम को संकलित नहीं कर सकता। – Dennis

उत्तर

12

सबसे पहले आप C99 इनलाइन मॉडल को समझना होगा - शायद आपके हेडर के साथ कुछ गड़बड़ है। वहाँ बाहरी (गैर स्थिर) लिंकेज

  • बाहरी परिभाषा
    एक समारोह की यह परिभाषा केवल एक नामित टीयू में पूरे कार्यक्रम में एक बार दिखाई दे सकता है, के साथ इनलाइन कार्यों के लिए परिभाषाओं के दो प्रकार हैं। यह एक निर्यातित कार्य प्रदान करता है जिसे अन्य टीयू से उपयोग किया जा सकता है।

  • इनलाइन परिभाषा
    ये हर टीयू जहां एक अलग परिभाषा के रूप में घोषित में दिखाई देते हैं। परिभाषा को एक-दूसरे या बाहरी परिभाषा के समान होने की आवश्यकता है। यदि लाइब्रेरी में आंतरिक उपयोग किया जाता है, तो वे फ़ंक्शन तर्कों पर जांच को छोड़ सकते हैं जो बाहरी परिभाषा में अन्यथा किया जाएगा। क्योंकि उनके स्थानीय घोषणाओं कोई लिंकेज (वे सी में की तरह बांटा नहीं जाता ++) है

समारोह के प्रत्येक परिभाषा, अपने स्वयं के स्थानीय स्थैतिक चर है। एक गैर स्थिर इनलाइन समारोह की एक परिभाषा एक इनलाइन परिभाषा हो सकता है अगर

  • एक टीयू में हर समारोह घोषणा विनिर्देशक inline, और
  • एक टीयू में कोई समारोह घोषणा विनिर्देशक extern शामिल भी शामिल है।

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

एक उदाहरण है कि दो टुस में दो बार inline की एक गलत उपयोग को दर्शाता है, क्योंकि यह एक समारोह के एक बाहरी परिभाषा भी शामिल है, एक बहु परिभाषा त्रुटि

// included into two TUs 
void f(void); // no inline specifier 
inline void f(void) { } 

क्योंकि संकलक है निम्नलिखित कार्यक्रम, खतरनाक है के कारण

: बाहरी परिभाषा का उपयोग करने के लिए, लेकिन कार्यक्रम प्रदान नहीं करता है एक

// main.c, only TU of the program 
inline void g(void) { 
    printf("inline definition\n"); 
} 

int main(void) { 
    g(); // could use external definition! 
} 

मैं जीसीसी का उपयोग कर कुछ परीक्षण मामलों है कि तंत्र आगे प्रदर्शित कर दिया है मुक्त

main.c

#include <stdio.h> 

inline void f(void); 

// inline definition of 'f' 
inline void f(void) { 
    printf("inline def main.c\n"); 
} 

// defined in TU of second inline definition 
void g(void); 

// defined in TU of external definition 
void h(void); 

int main(void) { 
    // unspecified whether external definition is used! 
    f(); 
    g(); 
    h(); 

    // will probably use external definition. But since we won't compare 
    // the address taken, the compiler can still use the inline definition. 
    // To prevent it, i tried and succeeded using "volatile". 
    void (*volatile fp)() = &f; 
    fp(); 
    return 0; 
} 

main1.c

#include <stdio.h> 

inline void f(void); 

// inline definition of 'f' 
inline void f(void) { 
    printf("inline def main1.c\n"); 
} 

void g(void) { 
    f(); 
} 

main2.c

#include <stdio.h> 

// external definition! 
extern inline void f(void); 

inline void f(void) { 
    printf("external def\n"); 
} 


void h(void) { 
    f(); // calls external def 
} 

अब, प्रोग्राम आउटपुट जो हम उम्मीद करते हैं!

$ gcc -std=c99 -O2 main.c main1.c main2.c 
inline def main.c 
inline def main1.c 
external def 
external def 

प्रतीक तालिका को देखते हुए, हम, कि एक इनलाइन परिभाषा का प्रतीक निर्यात नहीं किया है (main1.o से) देखेंगे, जबकि एक बाहरी परिभाषा (main2.o से) निर्यात किया जाता है।


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

static inline void f(void) { 
    printf("i'm unique in every TU\n"); 
} 
+0

यह एक बहुत अच्छा जवाब है। दुर्भाग्यवश, मैं अभी भी अपने प्रोग्राम को सफलतापूर्वक संकलित करने के लिए ज्ञान को लागू करने में सक्षम नहीं हूं - बाहरी इनलाइनों को स्थिर इनलाइनों में बदलने का प्रयास यह त्रुटि के साथ विफल हो जाता है कि यह एक ही फ़ंक्शन की स्थैतिक और nonstatic परिभाषाओं को मिला रहा है। शायद मैं अपने स्वयं के कोड में मैक्रोज़ का उपयोग उन कार्यों के नाम बदलने के लिए कर सकता हूं जहां मैं हेडर शामिल करता हूं, लेकिन हेडर में इन 100 चीजों में से कुछ हैं ... – Dennis

+0

अधिक जानकारी के लिए, यह चर्चा इंगित करती है कि संकलक अनुकूलन स्तर प्रभावित कर सकता है समस्या: http://lkml.indiana.edu/hypermail/linux/kernel/0408.0/1787.html – Dennis

+0

मुझे मिनीजीडब्ल्यू जीसीसी के साथ निर्माण करते समय कुछ मिल गया, और पाया कि मिनजीडब्ल्यू हेडर -std = gnu99 विकल्प। इसने लिंक करने का प्रयास करते समय बहुगुणित परिभाषित प्रतीकों के _lot_ का कारण बना दिया, क्योंकि कार्यों के लिए परिभाषा इस तरह से बनाई गई प्रत्येक ऑब्जेक्ट फ़ाइल में रखी गई थी। –