2012-04-02 17 views
9

में अपवाद के लिए त्रुटि के रूप में इरनो का उपयोग करते हुए अनपेक्षित प्रवाह नियंत्रण (कंपाइलर-बग?) त्रुटि कोड को परिवहन के लिए सी ++ अपवादों का उपयोग करते समय, संकलित कोड जो कोड के लिए g ++ (4.5.3) द्वारा उत्पन्न होता है जैसे कि निम्नलिखितसी ++ (जी ++)

#include <cerrno> 
#include <stdexcept> 
#include <string> 

class oserror : public std::runtime_error { 
private: 
    static std::string errnotostr(int errno_); 
public: 
    explicit oserror(int errno_) : 
     std::runtime_error(errnotostr(errno_)) { 
    } 
}; 

void test() { 
    throw oserror(errno); 
} 

बल्कि अप्रत्याशित रूप से (लिनक्स, x86_64 पर) कॉल पूर्ववर्ती __cxa_allocate_exception करने की वजह से

.type _Z4testv, @function 
    ... 
    movl $16, %edi 
    call __cxa_allocate_exception 
    movq %rax, %rbx 
    movq %rbx, %r12 
    call __errno_location 
    movl (%rax), %eax 
    movl %eax, %esi 
    movq %r12, %rdi 
    call _ZN7oserrorC1Ei 

क्या यह मूल रूप से मतलब है कि एक C++ अपवाद के लिए एक तर्क के रूप में errno काफी बेकार है है __errno_location पर कॉल (जो गलती की मैक्रो सामग्री है ओ), जहां पूर्व std :: malloc कॉल करता है और गलत स्थिति को सहेजता नहीं है (कम से कम जहां तक ​​मैं libstdC++ के eh_alloc.cc में __cxa_allocate_exception के स्रोतों को समझता हूं)।

इसका मतलब है कि अगर स्मृति आवंटन विफल हो जाता है, तो वास्तव में अपवाद ऑब्जेक्ट में पारित होने वाली त्रुटि संख्या std :: malloc सेट अप त्रुटि संख्या के साथ ओवरराइट हो जाती है। std :: malloc सफल निकास के मामले में, किसी भी मौजूदा त्रुटि स्थिति को सहेजने की कोई गारंटी नहीं देता है, इसलिए उपरोक्त कोड सामान्य मामले में निश्चित रूप से टूटा हुआ है।

Cygwin, 86, कोड संकलित हो जाता है कि परीक्षण के लिए ((यह भी जी ++ 4.5.3 का प्रयोग करके)) पर ठीक है, हालांकि यह है:

.def __Z4testv;  .scl 2;  .type 32;  .endef 
    ... 
    call ___errno 
    movl (%eax), %esi 
    movl $8, (%esp) 
    call ___cxa_allocate_exception 
    movl %eax, %ebx 
    movl %ebx, %eax 
    movl %esi, 4(%esp) 
    movl %eax, (%esp) 
    call __ZN7oserrorC1Ei 

इसका मतलब यह है कि ठीक से errno राज्य रैप करने के लिए पुस्तकालय कोड के लिए एक अपवाद में, मैं हमेशा एक मैक्रो जो

की तरह कुछ करने के लिए फैलता है का उपयोग करना होगा
int curerrno_ = errno; 
    throw oserror(curerrno_); 

मैं वास्तव में जो करने के मामले में मूल्यांकन आदेश के बारे में कुछ भी कहना है कि सी ++ मानक की इसी अनुभाग ढूंढें नहीं कर पा रहे अपवाद, लेकिन मेरे लिए ऐसा लगता है कि g86+ x86_64 पर उत्पन्न कोड (ओ एन लिनक्स) अपवाद ऑब्जेक्ट के लिए मेमोरी आवंटित करने के कारण इसके कन्स्ट्रक्टर के पैरामीटर एकत्र करने से पहले टूटा हुआ है, और यह किसी भी तरह से एक कंपाइलर बग है। क्या मैं सही हूं, या यह मेरे हिस्से पर कुछ मौलिक रूप से गलत सोच है?

+0

'errnotostr()' 'oserror 'का एक (स्थैतिक) फ़ंक्शन है जो मूल रूप से' strerror_r() 'कहता है और परिणाम के' std :: string'-ified रूप को देता है। मैंने कोड के उस हिस्से को शामिल नहीं किया है, क्योंकि यह उदाहरण के लिए अप्रासंगिक है। – modelnine

+0

हां, brainfart, क्षमा करें। उपेक्षा। – irobot

उत्तर

1

क्या यह मूल रूप से मतलब है कि एक C++ अपवाद के लिए एक तर्क के रूप में errno __errno_location करने के लिए कॉल (जो errno की मैक्रो सामग्री है), जहां पूर्व पूर्ववर्ती __cxa_allocate_exception करने के लिए कॉल की वजह से काफी बेकार है है std :: malloc को कॉल करता है और गलत स्थिति को सहेजता नहीं है (कम से कम जहां तक ​​मैं libstdC++ के eh_alloc.cc में __cxa_allocate_exception के स्रोतों को समझता हूं)।

यह सच नहीं है। जहां तक ​​मैंने स्रोत कोड की जांच की है, __cxa_allocate_exception के अंदर एकमात्र "चीज़" जो errno बदल सकती है malloc() है। दो मामलों में हो सकता है:

  • malloc() सफल होता है, तो errno अपरिवर्तित है;
  • malloc() विफल रहता है, तो std::terminate() कहा जाता है और आपका oserror() कभी नहीं बनाया जाता है।

इसलिए, अपने निर्माता बुला कार्यात्मक अपने कार्यक्रम में परिवर्तन नहीं करता से पहले _cxa_allocate_exception बुला के बाद से, मैं जी ++ ऐसा करने का अधिकार है विश्वास करते हैं।

+0

सबसे पहले, क्या कोई गारंटी है कि std :: malloc() आवंटित होने पर त्रुटि स्थिति नहीं बदलता है? यह शायद सोचने के लिए पागल है, लेकिन SuSv4 से AFAIK केवल गारंटी है कि वे इसे शून्य पर सेट नहीं करते हैं, न कि यह अपरिवर्तित है। और, वैसे भी, अगर std :: malloc() विफल रहता है, तो std :: terminate() को जरूरी नहीं कहा जाता है; कम-स्मृति स्थितियों में अपवाद बनाने के लिए उपयोग किया जाने वाला एक स्थिर बफर होता है, इसलिए अपवाद उठाए जाने के बावजूद इरनो सेट किया जा सकता है। – modelnine

+0

सबसे पहले, यदि मैं [POSIX विनिर्देश] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html) को सही तरीके से समझता हूं, तो 'malloc()' को केवल त्रुटि होने पर 'errno' सेट करना चाहिए। दूसरा, कोड जो कम-स्मृति स्थिति से संबंधित है केवल पैथ्रेड फ़ंक्शंस को कॉल करता है, जो त्रुटि कोड को वापसी मान के रूप में छोड़ देता है और 'errno' का उपयोग नहीं करता है। – user1202136

+0

यह बात है - malloc() का विनिर्देश केवल एक सकारात्मक उदाहरण देता है ("अन्यथा, यह एक शून्य सूचक लौटाएगा और त्रुटि को इंगित करने के लिए इरनो सेट करेगा"), जिस स्थिति में त्रुटि पहले से ही अधिलेखित हो चुकी है और मूल त्रुटि कोड कि मुझे oserror() में गुजरना पसंद आया होगा (और इससे कोई फर्क नहीं पड़ता कि pthread _ * - लोममेम पथ पर विधियां इस मामले में परिवर्तित नहीं होती हैं), लेकिन त्रुटि के मामले में, नहीं प्रावधान उस प्रावधान में किया जाता है, और फिर, AFAIK SuSv4, कोई प्रावधान नहीं है जो कहता है कि कार्यों को सफलतापूर्वक गलत नहीं बदलता है, बस यह शून्य पर सेट नहीं करता है। – modelnine

1

कृपया ध्यान दें कि __cxa_allocate_exception आपके कन्स्ट्रक्टर को वास्तव में बुलाया जाने से पहले किया जाता है।

32:std_errno.cpp ****  throw oserror(errno); 
352 0007 BF100000  movl $16, %edi 
;;;; Exception space allocation: 
355 000c E8000000  call __cxa_allocate_exception 
356 0011 4889C3  movq %rax, %rbx 
;;;; "errno" evaluation: 
357 0014 E8000000  call __errno_location 
358 0019 8B00   movl (%rax), %eax 
359 001b 89C6   movl %eax, %esi 
360 001d 4889DF  movq %rbx, %rdi 
;;;; Constructor called here: 
362 0020 E8000000  call _ZN7oserrorC1Ei 

तो यह समझ में आता है। __cxa_allocate_exception बस अपवाद के लिए स्थान आवंटित करता है, लेकिन इसे नहीं बनाता है (libc++abi Specification)।

जब आपका अपवाद ऑब्जेक्ट बनाया गया है, errno arleady मूल्यांकन किया गया है।

मैं अपने उदाहरण ले लिया और errnotostr कार्यान्वित:

// Unexpected flow of control (compiler-bug?) using errno as argument for exception in C++ (g++)

#include <cerrno> 
#include <stdexcept> 
#include <string> 

#include <iostream> 
#include <cstring> 
#include <sstream> 

class oserror : public std::runtime_error 
{ 
private: 
    static std::string errnotostr(int errno_) 
    { 
     std::stringstream ss; 

     ss << "[" << errno_ << "] " << std::strerror(errno_); 

     return ss.str(); 
    } 

public: 
    explicit oserror(int errno_) 
    : std::runtime_error(errnotostr(errno_)) 
    { 
    } 
}; 

void test() 
{ 
    throw oserror(errno); 
} 

int main() 
{ 
    try 
    { 
     std::cout << "Enter a value to errno: "; 
     std::cin >> errno; 

     std::cout << "Test with errno = " << errno << std::endl; 
     test(); 
    } 
    catch (oserror &o) 
    { 
     std::cout << "Exception caught: " << o.what() << std::endl; 
     return 1; 
    } 

    return 0; 
} 

तब मैं -O0 और -O2 साथ संकलित, चलाने के लिए और मिल गया एक ही परिणाम, सभी अपेक्षाओं के अनुसार:

> ./std_errno 
Enter a value to errno: 1 
Test with errno = 1Exception caught: [1] Operation not permitted 

> ./std_errno 
Enter a value to errno: 11 
Test with errno = 11 
Exception caught: [11] Resource temporarily unavailable 

> ./std_errno 
Enter a value to errno: 111 
Test with errno = 111 
Exception caught: [111] Connection refused 

(64-बिट्स ओपनस्यूज 12.1, जी ++ 4.6 पर चल रहा है।2)

+0

मैं इस तथ्य के बारे में शिकायत नहीं कर रहा हूं कि कन्स्ट्रक्टर में प्रवेश करते समय इरनो का पहले से मूल्यांकन नहीं किया गया है (मुझे पता है), बल्कि इरनो प्राप्त होने से पहले '__cxa_allocate_exception' को बुलाया जाता है, और इस तरह के बुनियादी ढांचे कोड _may_ त्रुटिपूर्ण स्थिति को बदलता है (प्रश्न पूछने से पहले std :: malloc() कहा जा रहा है) के बारे में चर्चा देखें, इसे चलाने का मौका मिलता है और फिर मूल्यांकन त्रुटि को कन्स्ट्रक्टर फ़ंक्शन में पास कर देता है। – modelnine

+0

मैं समझता हूं। मुझे स्पष्ट होने दो। यह ध्यान में रखते हुए कि अपवाद स्वयं 'errno' बदल सकता है, आपके मूल प्रश्न का उत्तर यह है: हां, आपको फेंकने से पहले' errno' का मान लेना होगा। मदद की उम्मीद है। – j4x

+0

उत्तर को पढ़ें जो मैंने अभी प्रश्न का उत्तर देने के रूप में चिह्नित किया है, और उस पर टिप्पणियां - प्रश्न 'इरनो' को बदलने वाले कन्स्ट्रक्टर के बारे में नहीं है, बल्कि 'फेंक' कथन के लिए उत्पन्न कोड के बारे में संभवतः इसके मूल्य से पहले इरनो बदल रहा है निर्माता को पास किया जाना चाहिए। धन्यवाद! – modelnine