2008-09-01 15 views
23

यदि मैं assert() का उपयोग करता हूं और दावा विफल रहता है तो assert()abort() पर कॉल करेगा, जो चल रहे प्रोग्राम को अचानक समाप्त कर देगा। मैं अपने उत्पादन कोड में बर्दाश्त नहीं कर सकता। क्या रनटाइम में जोर देने का कोई तरीका है, फिर भी असफल दावों को पकड़ने में सक्षम होने के लिए मुझे उनके पास शानदार तरीके से संभालने का मौका मिला है?मैं abort() का उपयोग किये बिना() कैसे जोर दे सकता हूं?

उत्तर

23

हां, वास्तव में ऐसा है। आपको कस्टम एक्टर्ट फ़ंक्शन स्वयं लिखना होगा, क्योंकि C++ के assert() वास्तव में abort() "फीचर" के साथ बंडल किया गया है। सौभाग्य से, यह आश्चर्यजनक रूप से सरल है।

Assert.hh

template <typename X, typename A> 
inline void Assert(A assertion) 
{ 
    if(!assertion) throw X(); 
} 

अगर एक विधेय नहीं रखता है ऊपर समारोह एक अपवाद फेंक देते हैं। इसके बाद आपको अपवाद पकड़ने का मौका मिलेगा। यदि आप अपवाद नहीं पकड़ते हैं, तो terminate() कहा जाएगा, जो प्रोग्राम को abort() के समान ही समाप्त कर देगा।

आप आश्चर्यचकित हो सकते हैं कि जब हम उत्पादन के लिए निर्माण कर रहे हैं तो दावा को दूर करने के बारे में क्या सोचते हैं। इस मामले में, आप स्थिरांक को परिभाषित कर सकते हैं जो दर्शाएगा कि आप उत्पादन के लिए निर्माण कर रहे हैं और फिर Assert() पर निरंतर संदर्भित करें।

debug.hh

#ifdef NDEBUG 
    const bool CHECK_WRONG = false; 
#else 
    const bool CHECK_WRONG = true; 
#endif 

main.cc

#include<iostream> 

struct Wrong { }; 

int main() 
{ 
    try { 
     Assert<Wrong>(!CHECK_WRONG || 2 + 2 == 5); 
     std::cout << "I can go to sleep now.\n"; 
    } 
    catch(Wrong e) { 
     std::cerr << "Someone is wrong on the internet!\n"; 
    } 

    return 0; 
} 

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

Assert() समारोह

if(!assertion) throw X(); 

टाइपिंग के बराबर है, लेकिन यह स्पष्ट रूप से प्रोग्रामर के इरादे इंगित करता है: एक अभिकथन बनाते हैं। सादे assert() एस की तरह, इस दृष्टिकोण के साथ grep को भी सम्मिलित करना आसान है।

इस तकनीक के बारे में अधिक जानकारी के लिए Bjarne Stroustrup की सी ++ प्रोग्रामिंग भाषा 3e, खंड 24.3.7.2 देखें।

+0

आप ही कभी मिल का निर्माण अपवाद डिफ़ॉल्ट, वहाँ नहीं इस के लिए बहुत कुछ कर सकते हैं लाभ है के बाद से जब से तुम के लिए किसी भी रन टाइम डाटा परिवहन नहीं कर सकते पकड़ साइट ... –

+0

इसे int बनाने के बजाए जोर तर्क को templatizing का जोड़ा मूल्य क्या है? वैसे भी अंततः int में परिवर्तित होना होगा। – JohnMcG

+0

@ ग्रेग रोजर्स बजेर्न स्ट्राउस्ट्रप आपके द्वारा उल्लिखित समस्या का समाधान प्रदान करता है: आप एक अनुकूलित ऑब्जेक्ट जोड़ सकते हैं जिसे आप Assert() के अतिरिक्त पैरामीटर के रूप में फेंकना चाहते हैं। नकारात्मकता यह है कि अतिरिक्त पैरामीटर के कारण Assert() अब assert() की तरह नहीं लगेगा। – wilhelmtell

6

सी/सी ++ में आवेषण केवल डीबग बिल्ड में चलाए जाते हैं। तो यह रनटाइम पर नहीं होगा। सामान्य आवेषणों में चीजों को चिह्नित करना चाहिए कि यदि वे एक बग इंगित करते हैं, और आमतौर पर आपके कोड आदि में धारणाएं दिखाते हैं।

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

+0

उत्पादन निर्माण में चल रहे या चलने वाले आवेषण एक कंपाइलर विकल्प नहीं है। ऑप्टिमाइज़ेशन विकल्पों के बावजूद वे निश्चित रूप से डिफ़ॉल्ट रूप से जीसीसी में चलते हैं। –

+2

मुझे व्यक्तिगत रूप से रिहाई मोड में आवेषण रखने का अच्छा विचार मिलता है। जब आप अपने क्लाइंट ऐप का उपयोग करते हैं तो आप यादृच्छिक दुर्घटनाओं को पसंद कर सकते हैं ... –

+2

विशेष रूप से यदि आपका प्रोग्राम अपरिभाषित व्यवहार का कारण बनता है तो वह दावा गलत हो जाता है; यह ग्राहक को कम से कम एक मूल्यवान त्रुटि संदेश और आसान प्रजनन कदम देने की अनुमति देगा; इस पर विचार करना पहले कभी नहीं हुआ था; और आपने सोचा (और गारंटी) यह नहीं होगा। – dcousens

10

glib's error reporting functions एक जोर के बाद जारी रखने का दृष्टिकोण ले लो। ग्लिब अंतर्निहित प्लेटफॉर्म आजादी लाइब्रेरी है जो जीनोम (जीटीके के माध्यम से) का उपयोग करता है। यहां एक मैक्रो है जो पूर्व शर्त की जांच करता है और पूर्व शर्त विफल होने पर एक स्टैक ट्रेस प्रिंट करता है।

char *doSomething(char *ptr) 
{ 
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL); // same as assert(ptr != NULL), but returns NULL if it fails. 

    if(ptr != NULL)  // Necessary if you want to define the macro only for debug builds 
    { 
     ... 
    } 

    return ptr; 
} 

void doSomethingElse(char *ptr) 
{ 
    RETURN_IF_FAIL(ptr != NULL); 
} 
:

void print_stack_trace(int fd) 
{ 
    void *array[256]; 
    size_t size; 

    size = backtrace (array, 256); 
    backtrace_symbols_fd(array, size, fd); 
} 

इस तरह आप मैक्रो का उपयोग होता है:

#define RETURN_IF_FAIL(expr)  do {     \ 
if (!(expr))           \ 
{              \ 
     fprintf(stderr,        \ 
       "file %s: line %d (%s): precondition `%s' failed.", \ 
       __FILE__,           \ 
       __LINE__,           \ 
       __PRETTY_FUNCTION__,        \ 
       #expr);            \ 
     print_stack_trace(2);          \ 
     return;             \ 
};    } while(0) 
#define RETURN_VAL_IF_FAIL(expr, val) do {       \ 
if (!(expr))              \ 
{                 \ 
     fprintf(stderr,            \ 
       "file %s: line %d (%s): precondition `%s' failed.",  \ 
       __FILE__,            \ 
       __LINE__,            \ 
       __PRETTY_FUNCTION__,         \ 
       #expr);             \ 
     print_stack_trace(2);           \ 
     return val;             \ 
};    } while(0) 

यहाँ समारोह है कि स्टैक ट्रेस प्रिंट, एक वातावरण के लिए लिखा जीएनयू toolchain (जीसीसी) का उपयोग करता है है

+3

@ विल्हेल्मटेल I मान लीजिए कि यह अब तक का सबसे अच्छा समाधान है क्योंकि मैं अपवाद का उपयोग करने से असहमत हूं। असाधारण कुछ असाधारण के लिए होना चाहिए, हालांकि असर संभालना नहीं है। इसके अलावा, अगर आप रिलीज बिल्ड में एस्सर्ट ऑन छोड़ने की योजना बनाते हैं, जो मुझे लगता है कि जब तक प्रदर्शन महत्वपूर्ण नहीं है, तो यह विधि अधिक कुशल होगी। आप इसे वैयक्तिकृत भी कर सकते हैं ताकि ऐसा होने पर आपके प्रोग्राम को बाहर निकलने की आवश्यकता न हो और आपको जानकारी वापस मिल जाएगी। मैं आवेषण और अपवादों पर व्यावहारिक प्रोग्रामर पुस्तक के अध्याय को पढ़ने पर सुझाव देता हूं। इसके अलावा [यह लिंक] (http://www.gotw.ca/publications/mill22.htm)। – ForceMagic

5

यहाँ क्या मैं अपने में "assert.h" है है (मैक ओएस 10.4):

#define assert(e) ((void) ((e) ? 0 : __assert (#e, __FILE__, __LINE__))) 
#define __assert(e, file, line) ((void)printf ("%s:%u: failed assertion `%s'\n", file, line, e), abort(), 0) 

उस पर आधारित, कॉल को एक फेंक (अपवाद) द्वारा abort() को प्रतिस्थापित करें। और printf के बजाय आप स्ट्रिंग को अपवाद के त्रुटि संदेश में प्रारूपित कर सकते हैं। अंत में, आपको कुछ ऐसा मिलता है:

#define assert(e) ((void) ((e) ? 0 : my_assert (#e, __FILE__, __LINE__))) 
#define my_assert(e, file, line) (throw std::runtime_error(\ 
    std::string(file:)+boost::lexical_cast<std::string>(line)+": failed assertion "+e)) 

मैंने इसे संकलित करने की कोशिश नहीं की है, लेकिन आपको इसका अर्थ मिल गया है।

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

void my_assert(const char* e, const char* file, int line); 

और यह कहीं न कहीं लागू जहां आप स्वतंत्र रूप से सभी हेडर की आवश्यकता शामिल कर सकते हैं।

अगर आपको इसकी आवश्यकता है तो इसे #ifdef DEBUG में लपेटें, या नहीं, यदि आप हमेशा उन चेक को चलाने के लिए चाहते हैं।

+0

तो यह तर्क देना बाकी है कि एक मैक्रो टेम्पलेट फ़ंक्शन से बेहतर है या नहीं। इसके अलावा, अनुक्रम ऑपरेटर का अर्थ है कि ऑपरेंड किसी भी मनमाने ढंग से क्रम में किया जा सकता है। मुझे संदेह है कि __asert() में आपका क्या मतलब है। – wilhelmtell

+0

पहले ब्लॉक में "__assert" फ़ंक्शन मेरे सिस्टम हेडर (0 एस 10.4) से आता है, मुझे उम्मीद है कि वे जानते हैं कि वे क्या कर रहे हैं। मैक्रो फ़ाइल और लाइन जानकारी प्राप्त करना आसान बनाता है। लेकिन किसी बिंदु पर यह काफी कॉस्मेटिक हो जाता है ... – rlerallut

-1
_set_error_mode(_OUT_TO_MSGBOX); 

मेरा मानना ​​है कि यह कार्य आपकी मदद कर सकता है।

+0

यह विंडोज़ केवल एक समारोह प्रतीत होता है: [लिंक] (https://msdn.microsoft.com/en-us/library/aa235388 (v = vs.60)। एएसपीएक्स) –

+0

और कम से कम आपको यह समझाना चाहिए कि हेडर/कंपाइलर/इसका उपयोग करने के लिए जो भी आवश्यक है। –

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

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