2011-10-13 11 views
83

मैंने एक समान question से पहले इंटरफ़ेस चर के बारे में बहुत पहले नहीं पूछा था।निहित इंटरफेस चर के संकलक उपचार दस्तावेज है?

इस प्रश्न का स्रोत मेरे कोड में एक बग था क्योंकि मुझे संकलक द्वारा बनाए गए एक अंतर्निहित इंटरफ़ेस चर के अस्तित्व के बारे में पता नहीं था। इस चर को अंतिम रूप दिया गया था जब इसकी प्रक्रिया समाप्त हो गई थी। इसके बदले में चर के जीवनकाल की वजह से एक बग का कारण बन गया था जो मैंने अनुमान लगाया था।

program ImplicitInterfaceLocals; 

{$APPTYPE CONSOLE} 

uses 
    Classes; 

function Create: IInterface; 
begin 
    Result := TInterfacedObject.Create; 
end; 

procedure StoreToLocal; 
var 
    I: IInterface; 
begin 
    I := Create; 
end; 

procedure StoreViaPointerToLocal; 
var 
    I: IInterface; 
    P: ^IInterface; 
begin 
    P := @I; 
    P^ := Create; 
end; 

begin 
    StoreToLocal; 
    StoreViaPointerToLocal; 
end. 

StoreToLocal बस के रूप में आप कल्पना कर सकते हैं संकलित किया गया है:

अब, मैं संकलक से कुछ दिलचस्प व्यवहार को वर्णन करने के लिए एक सरल परियोजना है। स्थानीय चर I, फ़ंक्शन का परिणाम Create पर एक अंतर्निहित var पैरामीटर के रूप में पारित किया गया है। StoreToLocal के लिए साफ-सुथरा परिणाम एक ही कॉल में IntfClear पर होता है। वहां कोई आश्चर्य नहीं है।

हालांकि, StoreViaPointerToLocal का अलग-अलग व्यवहार किया जाता है। कंपाइलर एक अंतर्निहित स्थानीय चर बनाता है जो यह Create तक जाता है। जब Create रिटर्न, P^ पर असाइनमेंट किया जाता है। यह इंटरफ़ेस के संदर्भ वाले दो स्थानीय चर के साथ दिनचर्या छोड़ देता है। StoreViaPointerToLocal के लिए साफ-सुथरा परिणाम दो कॉल में IntfClear पर है।

StoreViaPointerToLocal के लिए

संकलित कोड इस तरह है:

ImplicitInterfaceLocals.dpr.24: begin 
00435C50 55    push ebp 
00435C51 8BEC    mov ebp,esp 
00435C53 6A00    push $00 
00435C55 6A00    push $00 
00435C57 6A00    push $00 
00435C59 33C0    xor eax,eax 
00435C5B 55    push ebp 
00435C5C 689E5C4300  push $00435c9e 
00435C61 64FF30   push dword ptr fs:[eax] 
00435C64 648920   mov fs:[eax],esp 
ImplicitInterfaceLocals.dpr.25: P := @I; 
00435C67 8D45FC   lea eax,[ebp-$04] 
00435C6A 8945F8   mov [ebp-$08],eax 
ImplicitInterfaceLocals.dpr.26: P^ := Create; 
00435C6D 8D45F4   lea eax,[ebp-$0c] 
00435C70 E873FFFFFF  call Create 
00435C75 8B55F4   mov edx,[ebp-$0c] 
00435C78 8B45F8   mov eax,[ebp-$08] 
00435C7B E81032FDFF  call @IntfCopy 
ImplicitInterfaceLocals.dpr.27: end; 
00435C80 33C0    xor eax,eax 
00435C82 5A    pop edx 
00435C83 59    pop ecx 
00435C84 59    pop ecx 
00435C85 648910   mov fs:[eax],edx 
00435C88 68A55C4300  push $00435ca5 
00435C8D 8D45F4   lea eax,[ebp-$0c] 
00435C90 E8E331FDFF  call @IntfClear 
00435C95 8D45FC   lea eax,[ebp-$04] 
00435C98 E8DB31FDFF  call @IntfClear 
00435C9D C3    ret 

मैं क्यों संकलक इस कर रही है के रूप में अनुमान लगा सकते हैं। जब यह साबित हो सकता है कि परिणाम परिवर्तक को असाइन करने से अपवाद नहीं बढ़ेगा (यानी यदि चर एक स्थानीय है) तो यह परिणाम चर का उपयोग सीधे करता है। अन्यथा यह एक निहित स्थानीय का उपयोग करता है और फ़ंक्शन वापस लौटने के बाद इंटरफ़ेस की प्रतिलिपि बनाता है जिससे यह सुनिश्चित किया जाता है कि हम अपवाद के मामले में संदर्भ को रिसाव नहीं करते हैं।

लेकिन मुझे दस्तावेज़ में इसका कोई विवरण नहीं मिल रहा है। यह महत्वपूर्ण है क्योंकि इंटरफ़ेस जीवनकाल महत्वपूर्ण है और एक प्रोग्रामर के रूप में आपको अवसर पर इसे प्रभावित करने में सक्षम होना चाहिए।

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

अद्यतन 1

रेमी के सवाल का जवाब करने के लिए, यह मेरे लिए मायने रखता है जब मैं एक और अंतिम रूप दिए जाने से बाहर ले जाने से पहले इंटरफ़ेस के पीछे वस्तु को अंतिम रूप देने की जरूरत है।

begin 
    AcquirePythonGIL; 
    try 
    PyObject := CreatePythonObject; 
    try 
     //do stuff with PyObject 
    finally 
     Finalize(PyObject); 
    end; 
    finally 
    ReleasePythonGIL; 
    end; 
end; 

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

+7

पता नहीं क्यों यह डाउनवॉट किया गया था, इसके अलावा सवाल वास्तव में जटिल है। मेरे सिर पर रास्ता होने के लिए ऊपर उठाया। मुझे पता है कि वास्तव में इस तरह के आर्केनम के परिणामस्वरूप एक साल पहले मैंने एक ऐप में कुछ सूक्ष्म संदर्भ गिनती बग का परिणाम दिया था। हमारे सबसे अच्छे गीकों में से एक ने इसे समझने में घंटों बिताए। अंत में हमने इसके आसपास काम किया लेकिन कभी समझ नहीं आया कि संकलक कैसे काम करने का इरादा रखता था। –

+0

जब आप संदर्भ-गिनती इंटरफेस के साथ काम करते हैं तो आपको यह नहीं मानना ​​चाहिए कि कोई अन्य ग्राहक नहीं है (आपके मामले में कंपाइलर) जिसमें उनके स्वयं के इंटरफ़ेस संदर्भ हैं। यदि सभी ग्राहक सही ढंग से 'AddRef'/'रिलीज' करते हैं, तो सभी को ठीक काम करना चाहिए। अन्यथा यह एक बग है (आपकी बग जब से मुझे लगता है कि संकलक सही संदर्भ गिनती करता है)। – kludg

+3

@ सर्ग कंपाइलर ने इसका संदर्भ पूरी तरह से गिन रहा था। समस्या यह थी कि एक अतिरिक्त चर था जिसमें एक संदर्भ था जिसे मैं नहीं देख सका। मैं जो जानना चाहता हूं वह इतना अतिरिक्त, छुपा, संदर्भ लेने के लिए संकलक को उत्तेजित करता है। –

उत्तर

13

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

procedure UseInterface(foo: IInterface); 
begin 
end; 

procedure Test() 
begin 
    UseInterface(Create()); 
end; 

संकलक बनाएं का परिणाम धारण करने के लिए एक अंतर्निहित अस्थायी चर बनाने के लिए है के रूप में यह UseInterface में पारित हो जाता है, सुनिश्चित करें इंटरफ़ेस एक जीवन भर है कि बनाने के लिए> = UseInterface कॉल के जीवनकाल । टेस्ट() प्रक्रिया के अंत में इस मामले में, उस अंतर्निहित अस्थायी चर को उस प्रक्रिया के अंत में निपटाया जाएगा, जिसका मालिक है।

यह संभव है कि आपका पॉइंटर असाइनमेंट केस समान बाल्टी में हो सकता है क्योंकि इंटरमीडिएट इंटरफ़ेस मानों को फ़ंक्शन पैरामीटर के रूप में पास किया जा रहा है, क्योंकि संकलक "देख" नहीं सकता है कि मूल्य कहां जा रहा है।

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

0

आप गारंटी नहीं दे सकते कि संकलक एक अस्थायी अदृश्य चर बनाने का निर्णय नहीं लेगा।

और यहां तक ​​कि यदि आप करते हैं, तो बंद ऑप्टिमाइज़ेशन (या यहां तक ​​कि फ्रेम ढेर?) आपके पूरी तरह से चेक किए गए कोड को गड़बड़ कर सकता है।

और यहां तक ​​कि यदि आप परियोजना विकल्पों के सभी संभावित संयोजनों के तहत अपने कोड की समीक्षा करना प्रबंधित करते हैं - लाजर या यहां तक ​​कि नए डेल्फी संस्करण जैसे कुछ कोड को संकलित करने से नरक वापस आ जाएगा।

एक सर्वोत्तम शर्त का उपयोग करना होगा "आंतरिक चर नियमित रूप से बाहर नहीं जा सकते" नियम। हम आमतौर पर नहीं जानते हैं, अगर कंपाइलर कुछ आंतरिक चर बनाएगा या नहीं, लेकिन हम जानते हैं कि नियमित रूप से मौजूद होने पर ऐसा कोई भी चर (यदि बनाया गया है) को अंतिम रूप दिया जाएगा।

इसलिए, यदि आप इस तरह कोड है:

// 1. Some code which may (or may not) create invisible variables 
// 2. Some code which requires release of reference-counted data 

उदाहरण के लिए:

Lib := LoadLibrary(Lib, 'xyz'); 
try 
    // Create interface 
    P := GetProcAddress(Lib, 'xyz'); 
    I := P; 
    // Work with interface 
finally 
    // Something that requires all interfaces to be released 
    FreeLibrary(Lib); // <- May be not OK 
end; 

तो फिर तुम सिर्फ लपेट सबरूटीन में ब्लॉक "इंटरफेस के साथ कार्य करें" चाहिए:

procedure Work(const Lib: HModule); 
begin 
    // Create interface 
    P := GetProcAddress(Lib, 'xyz'); 
    I := P; 
    // Work with interface 
end; // <- Releases hidden variables (if any exist) 

Lib := LoadLibrary(Lib, 'xyz'); 
try 
    Work(Lib); 
finally 
    // Something that requires all interfaces to be released 
    FreeLibrary(Lib); // <- OK! 
end; 

यह एक सरल, लेकिन प्रभावी नियम है।

+0

मेरे परिदृश्य में, I: = CreateInterfaceFromLib (...) के परिणामस्वरूप एक निहित स्थानीय था। तो आप जो सुझाव देते हैं वह मदद नहीं करेगा। किसी भी मामले में, मैंने पहले ही प्रश्न में एक कामकाज स्पष्ट रूप से प्रदर्शित किया है। फ़ंक्शन स्कोप द्वारा नियंत्रित होने वाले निहित स्थानीय लोगों के जीवनकाल पर आधारित एक। मेरा प्रश्न उन परिदृश्यों से संबंधित है जो अंतर्निहित स्थानीय लोगों के लिए नेतृत्व करेंगे। –

+0

मेरा मुद्दा यह था कि यह पहली जगह पूछने का गलत सवाल है। – Alex

+0

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

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