C में कथनों को पकड़ने का प्रयास करें


101

मैं आज कोशिश कर रहा था कि दूसरी भाषाओं में मौजूद प्रचलित / पकड़ने वाले ब्लॉक के बारे में सोचूं। थोड़ी देर के लिए इस googled लेकिन कोई परिणाम नहीं के साथ। जो मुझे पता है, उसमें से सी में कोशिश / पकड़ने जैसी कोई बात नहीं है। हालाँकि, क्या उन्हें "अनुकरण" करने का कोई तरीका है?
ज़रूर, वहाँ जोरदार और अन्य चालें हैं, लेकिन कोशिश / पकड़ने जैसा कुछ भी नहीं है, जो उठाए गए अपवाद को भी पकड़ता है। धन्यवाद


3
अपवाद जैसे तंत्र आम तौर पर एक तंत्र के बिना उपयोगी नहीं होने जा रहे हैं स्वचालित रूप से मुक्त संसाधनों के लिए जब स्टैक निराधार है। C ++ RAII का उपयोग करता है; जावा, C #, पायथन आदि कचरा बीनने वालों का उपयोग करते हैं। (और ध्यान दें कि कचरा संग्रहकर्ता केवल स्मृति को मुक्त करता है। स्वचालित रूप से अन्य प्रकार के संसाधनों को मुक्त करने के लिए, वे
फाइनल

@jamesdlin, हम C के साथ RAII क्यों नहीं कर सके?
पचेरियर

1
@Pacerier RAII को स्वचालित रूप से कॉलिंग फ़ंक्शन की आवश्यकता होती है जब ऑब्जेक्ट्स नष्ट हो जाते हैं (यानी, विध्वंसक)। आप सी में ऐसा करने का प्रस्ताव कैसे देते हैं?
jamesdlin

जवाबों:


90

C स्वयं अपवादों का समर्थन नहीं करता है, लेकिन आप उन्हें कॉल setjmpऔर longjmpकॉल के साथ एक हद तक अनुकरण कर सकते हैं।

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened here\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjmp(s_jumpBuffer, 42);
}

इस वेबसाइट में अपवादों का अनुकरण करने के तरीके के बारे में एक अच्छा ट्यूटोरियल है setjmpऔरlongjmp


1
भयानक समाधान! क्या यह समाधान पार है? इसने मेरे लिए MSVC2012 पर काम किया लेकिन MacOSX क्लैंग कंपाइलर में नहीं।
mannysz

1
मुझे इसमें सुराग दें: मैंने सोचा कि क्लॉस पकड़ने की कोशिश करें आपको अपवादों को पकड़ने की अनुमति मिलती है (जैसे शून्य से विभाजित करना)। यह फ़ंक्शन केवल अपवादों को पकड़ने की अनुमति देता है जो आप खुद को फेंकते हैं। लॉन्गजम्प को सही कहकर असली अपवाद नहीं फेंके जाते? अगर मैं इस कोड का उपयोग कुछ ऐसा करने के लिए करता हूँ जैसे try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')}; यह सही काम नहीं करेगा?
सैम

शून्य से डिवाइड C ++ में भी एक अपवाद नहीं है, इसे संभालने के लिए आपको या तो यह जांचने की आवश्यकता है कि विभाजक शून्य नहीं है और इसे संभाल लें या जब आप शून्य सूत्र द्वारा डिवाइड चलाते हैं, तो उस SIGFPE को संभाल लें।
जेम्स

25

आप इसी तरह की त्रुटि से निपटने के लिए सी में गोटो का उपयोग करते हैं ।
यह अपवादों का निकटतम समतुल्य है जो आप C में प्राप्त कर सकते हैं।


3
@JensGustedt यह वही है जो गोटो वर्तमान में बहुत बार और उदाहरण के लिए उपयोग किया जाता है जहां यह समझ में आता है (सेटजम्प / एलजेएमपी बेहतर विकल्प है, लेकिन लेबल + गोटो आमतौर पर अधिक उपयोग किया जाता है)।
टॉमस प्रूजिना

1
@AoeAoe, शायद gotoत्रुटि से निपटने के लिए अधिक उपयोग किया जाता है, लेकिन इतना क्या? प्रश्न त्रुटि से निपटने के बारे में नहीं है, बल्कि स्पष्ट रूप से कोशिश / पकड़ समकक्षों के बारे में है। gotoयह समान फ़ंक्शन के लिए प्रतिबंधित होने के बाद से एक समतुल्य फ्रॉ ट्राई / कैच नहीं है।
जेन्स गुस्तेद

@JensGustedt I थोड़े ने गोटो से नफरत / डर के प्रति प्रतिक्रिया व्यक्त की और इसका इस्तेमाल करने वाले लोगों (मेरे शिक्षकों ने मुझे विश्वविद्यालय पर गोटो के उपयोग की डरावनी कहानियाँ भी सुनाईं)। [OT] केवल एक चीज जो वास्तव में जोखिम भरी है, और गोटो के बारे में 'बादल' वाली बात 'गोटो बैकवर्ड' है, लेकिन मैंने देखा है कि लिनक्स वीएफएस (गिट दोष आदमी ने कसम खाई थी कि यह प्रदर्शन महत्वपूर्ण-लाभकारी था)।
टॉमस प्रूजिना

एक आधुनिक, व्यापक रूप से स्वीकृत, सहकर्मी की समीक्षा किए गए स्रोत में उपयोग किए जाने वाले ट्राइ / कैच मैकेनिज्म के वैध उपयोगों के लिए व्यवस्थित स्रोत देखें gotogotoएक "फेंक" समकक्ष के लिए खोजें , और finishएक "पकड़" समकक्ष के लिए।
स्टीवर्ट

13

ठीक है, मैं इसका जवाब नहीं दे सका। मुझे पहले बताएं कि मुझे नहीं लगता कि सी में इसे अनुकरण करना एक अच्छा विचार है क्योंकि यह वास्तव में सी के लिए एक विदेशी अवधारणा है।

हम C ++ कोशिश / फेंक / कैच के सीमित संस्करण का उपयोग करने के लिए प्रीप्रोसेसर और स्थानीय स्टैक चर का दुरुपयोग कर सकते हैं

संस्करण 1 (स्थानीय गुंजाइश फेंकता है)

#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

संस्करण 1 केवल एक स्थानीय फेंक है (फ़ंक्शन के दायरे को नहीं छोड़ सकता)। यह कोड में चर घोषित करने की C99 की क्षमता पर निर्भर करता है (यदि फ़ंक्शन में यह पहली चीज़ है तो C89 में काम करना चाहिए)।

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

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

#include <stdio.h>
#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

int main(void)
{
    try
    {
        printf("One\n");
        throw();
        printf("Two\n");
    }
    catch(...)
    {
        printf("Error\n");
    }
    return 0;
}

यह कुछ इस तरह से काम करता है:

int main(void)
{
    bool HadError=false;
    {
        printf("One\n");
        HadError=true;
        goto ExitJmp;
        printf("Two\n");
    }
ExitJmp:
    if(HadError)
    {
        printf("Error\n");
    }
    return 0;
}

संस्करण 2 (गुंजाइश कूद)

#include <stdbool.h>
#include <setjmp.h>

jmp_buf *g__ActiveBuf;

#define try jmp_buf __LocalJmpBuff;jmp_buf *__OldActiveBuf=g__ActiveBuf;bool __WasThrown=false;g__ActiveBuf=&__LocalJmpBuff;if(setjmp(__LocalJmpBuff)){__WasThrown=true;}else
#define catch(x) g__ActiveBuf=__OldActiveBuf;if(__WasThrown)
#define throw(x) longjmp(*g__ActiveBuf,1);

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

उदाहरण का फिर से विस्तार हुआ:

jmp_buf *g_ActiveBuf;

int main(void)
{
    jmp_buf LocalJmpBuff;
    jmp_buf *OldActiveBuf=g_ActiveBuf;
    bool WasThrown=false;
    g_ActiveBuf=&LocalJmpBuff;

    if(setjmp(LocalJmpBuff))
    {
        WasThrown=true;
    }
    else
    {
        printf("One\n");
        longjmp(*g_ActiveBuf,1);
        printf("Two\n");
    }
    g_ActiveBuf=OldActiveBuf;
    if(WasThrown)
    {
        printf("Error\n");
    }
    return 0;
}

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

इस कोड का उपयोग करने के कई पक्ष हैं (लेकिन यह एक मज़ेदार मानसिक व्यायाम है):

  • यह आवंटित मेमोरी को मुक्त नहीं करेगा क्योंकि इसमें कोई डिकंस्ट्रक्टर नहीं हैं।
  • आपके पास एक से अधिक 1 प्रयास / कैच एक दायरे में हो सकते हैं (कोई नेस्टिंग नहीं)
  • आप वास्तव में C ++ जैसे अपवाद या अन्य डेटा नहीं फेंक सकते
  • धागा सुरक्षित नहीं है
  • आप विफलता के लिए अन्य प्रोग्रामर सेट कर रहे हैं क्योंकि वे संभवतः हैक को नोटिस नहीं करेंगे और उनका उपयोग करने की कोशिश करेंगे जैसे कि C ++ try / catch ब्लॉक।

अच्छा वैकल्पिक समाधान।
हसीबी मीर

संस्करण 1 अच्छा विचार है, लेकिन उस __HadError चर को रीसेट या स्कोप करना होगा। अन्यथा आप एक ही ब्लॉक में एक से अधिक ट्राइ-कैच का उपयोग नहीं कर पाएंगे। शायद जैसे एक वैश्विक समारोह का उपयोग करें bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}। लेकिन स्थानीय वैरिएबल को भी फिर से परिभाषित किया जाएगा, इसलिए चीजें थोड़ा हाथ से निकल जाएंगी।
फलामेव 1000

हां, यह एक ही फ़ंक्शन में एक कोशिश-कैच तक सीमित है। एक बड़ा मुद्दा तब वैरिएबल हालांकि लेबल है क्योंकि आप एक ही फ़ंक्शन में डुप्लिकेट लेबल नहीं कर सकते हैं।
पॉल हचिंसन

10

C99 में, आप गैर-स्थानीय नियंत्रण प्रवाह के लिए setjmp/ उपयोग कर सकते हैं longjmp

एक भी गुंजाइश, सामान्य, कोडिंग कई संसाधन आवंटन और अनेक निकास का उपयोग करता है की उपस्थिति में सी के लिए पैटर्न संरचित के भीतर goto, जैसे इस उदाहरण में । यह इसी तरह है कि कैसे सी + + हुड के नीचे स्वचालित वस्तुओं के विध्वंसक कॉल को लागू करता है, और यदि आप इस परिश्रम से चिपके रहते हैं, तो यह आपको जटिल कार्यों में भी कुछ हद तक सफाई के लिए अनुमति देनी चाहिए।


5

जबकि कुछ अन्य उत्तरों ने सरल मामलों का उपयोग किया है setjmpऔर longjmp, एक वास्तविक एप्लिकेशन में दो चिंताएं हैं जो वास्तव में मायने रखती हैं।

  1. कोशिश / पकड़ने के ब्लॉक के घोंसले के शिकार। आपके लिए एकल वैश्विक चर का उपयोग jmp_bufकरने से ये काम नहीं करेंगे।
  2. थ्रेडिंग। आपके लिए एक एकल वैश्विक चर jmp_bufइस स्थिति में सभी प्रकार के दर्द का कारण होगा।

इनका समाधान यह है jmp_bufकि आप जाते समय एक थ्रेड-लोकल स्टैक को अपडेट रखें। (मुझे लगता है कि यह वही है जो लुआ आंतरिक रूप से उपयोग करता है)।

तो इसके बजाय (JaredPar के भयानक जवाब से)

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjump(s_jumpBuffer, 42);
}

आप कुछ इस तरह का उपयोग करेंगे:

#define MAX_EXCEPTION_DEPTH 10;
struct exception_state {
  jmp_buf s_jumpBuffer[MAX_EXCEPTION_DEPTH];
  int current_depth;
};

int try_point(struct exception_state * state) {
  if(current_depth==MAX_EXCEPTION_DEPTH) {
     abort();
  }
  int ok = setjmp(state->jumpBuffer[state->current_depth]);
  if(ok) {
    state->current_depth++;
  } else {
    //We've had an exception update the stack.
    state->current_depth--;
  }
  return ok;
}

void throw_exception(struct exception_state * state) {
  longjump(state->current_depth-1,1);
}

void catch_point(struct exception_state * state) {
    state->current_depth--;
}

void end_try_point(struct exception_state * state) {
    state->current_depth--;
}

__thread struct exception_state g_exception_state; 

void Example() { 
  if (try_point(&g_exception_state)) {
    catch_point(&g_exception_state);
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
    end_try_point(&g_exception_state);
  }
}

void Test() {
  // Rough equivalent of `throw`
  throw_exception(g_exception_state);
}

फिर इस के एक और अधिक यथार्थवादी संस्करण में त्रुटि जानकारी संग्रहीत करने का कुछ तरीका शामिल होगा exception_state, बेहतर हैंडलिंग MAX_EXCEPTION_DEPTH(शायद बफर को विकसित करने के लिए रीअलॉक का उपयोग करना, या ऐसा कुछ)।

अस्वीकरण: ऊपर दिए गए कोड को बिना किसी परीक्षण के लिखा गया था। यह विशुद्ध रूप से है इसलिए आपको इस बात का अंदाजा है कि चीजों को कैसे तैयार किया जाता है। अलग-अलग सिस्टम और अलग-अलग कंपाइलरों को थ्रेड लोकल स्टोरेज को अलग तरीके से लागू करना होगा। कोड में संभवतः संकलित त्रुटियां और तर्क त्रुटियां शामिल हैं - इसलिए जब तक आप इसका उपयोग करने के लिए स्वतंत्र होते हैं, जैसा कि आप चुनते हैं, इसे उपयोग करने से पहले परीक्षण करें;)


4

एक त्वरित Google खोज इस तरह के kludgey समाधान देता है जैसे कि अन्य लोगों ने उल्लेख किया है कि setjmp / longjmp का उपयोग करें। सी ++ / जावा की कोशिश / पकड़ के रूप में सरल और सुरुचिपूर्ण कुछ भी नहीं। मैं खुद को संभालने के लिए एडीए के अपवाद के बजाय आंशिक हूं।

बयानों के साथ सब कुछ जांचें :)


4

यह setjmp/longjmpसी। P99 के साथ किया जा सकता है, इसके लिए काफी आरामदायक टूलसेट है जो C11 के नए थ्रेड मॉडल के अनुरूप है।


2

यह C में त्रुटि से निपटने का एक और तरीका है जो setjmp / longjmp का उपयोग करने से अधिक प्रदर्शन करने वाला है। दुर्भाग्य से, यह MSVC के साथ काम नहीं करेगा लेकिन यदि केवल GCC / Clang का उपयोग करना एक विकल्प है, तो आप इस पर विचार कर सकते हैं। विशेष रूप से, यह "लेबल मान के रूप में" एक्सटेंशन का उपयोग करता है, जो आपको एक लेबल का पता लेने की अनुमति देता है, इसे एक मूल्य में संग्रहीत करता है और बिना शर्त के कूद जाता है। मैं इसे एक उदाहरण का उपयोग करके प्रस्तुत करूँगा:

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    /* Declare an error handler variable. This will hold the address
       to jump to if an error occurs to cleanup pending resources.
       Initialize it to the err label which simply returns an
       error value (NULL in this example). The && operator resolves to
       the address of the label err */
    void *eh = &&err;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    if (!engine)
        goto *eh; /* this is essentially your "throw" */

    /* Now make sure that if we throw from this point on, the memory
       gets deallocated. As a convention you could name the label "undo_"
       followed by the operation to rollback. */
    eh = &&undo_malloc;

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    if (!engine->window)
        goto *eh;   /* The neat trick about using approach is that you don't
                       need to remember what "undo" label to go to in code.
                       Simply go to *eh. */

    eh = &&undo_window_open;

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

यदि आप चाहें, तो आप अपने स्वयं के त्रुटि-हैंडलिंग प्रणाली को प्रभावी ढंग से लागू करने में, सामान्य कोड को परिभाषित कर सकते हैं।

/* Put at the beginning of a function that may fail. */
#define declthrows void *_eh = &&err

/* Cleans up resources and returns error result. */
#define throw goto *_eh

/* Sets a new undo checkpoint. */
#define undo(label) _eh = &&undo_##label

/* Throws if [condition] evaluates to false. */
#define check(condition) if (!(condition)) throw

/* Throws if [condition] evaluates to false. Then sets a new undo checkpoint. */
#define checkpoint(label, condition) { check(condition); undo(label); }

फिर उदाहरण बनता है

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    declthrows;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    checkpoint(malloc, engine);

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    checkpoint(window_open, engine->window);

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

2

चेतावनी: निम्नलिखित बहुत अच्छा नहीं है, लेकिन यह काम करता है।

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    unsigned int  id;
    char         *name;
    char         *msg;
} error;

#define _printerr(e, s, ...) fprintf(stderr, "\033[1m\033[37m" "%s:%d: " "\033[1m\033[31m" e ":" "\033[1m\033[37m" " ‘%s_error’ " "\033[0m" s "\n", __FILE__, __LINE__, (*__err)->name, ##__VA_ARGS__)
#define printerr(s, ...) _printerr("error", s, ##__VA_ARGS__)
#define printuncaughterr() _printerr("uncaught error", "%s", (*__err)->msg)

#define _errordef(n, _id) \
error* new_##n##_error_msg(char* msg) { \
    error* self = malloc(sizeof(error)); \
    self->id = _id; \
    self->name = #n; \
    self->msg = msg; \
    return self; \
} \
error* new_##n##_error() { return new_##n##_error_msg(""); }

#define errordef(n) _errordef(n, __COUNTER__ +1)

#define try(try_block, err, err_name, catch_block) { \
    error * err_name = NULL; \
    error ** __err = & err_name; \
    void __try_fn() try_block \
    __try_fn(); \
    void __catch_fn() { \
        if (err_name == NULL) return; \
        unsigned int __##err_name##_id = new_##err##_error()->id; \
        if (__##err_name##_id != 0 && __##err_name##_id != err_name->id) \
            printuncaughterr(); \
        else if (__##err_name##_id != 0 || __##err_name##_id != err_name->id) \
            catch_block \
    } \
    __catch_fn(); \
}

#define throw(e) { *__err = e; return; }

_errordef(any, 0)

उपयोग:

errordef(my_err1)
errordef(my_err2)

try ({
    printf("Helloo\n");
    throw(new_my_err1_error_msg("hiiiii!"));
    printf("This will not be printed!\n");
}, /*catch*/ any, e, {
    printf("My lovely error: %s %s\n", e->name, e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err2_error_msg("my msg!"));
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printerr("%s", e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err1_error());
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printf("Catch %s if you can!\n", e->name);
})

आउटपुट:

Helloo
My lovely error: my_err1 hiiiii!

Helloo
/home/naheel/Desktop/aa.c:28: error: my_err2_error my msg!

Helloo
/home/naheel/Desktop/aa.c:38: uncaught error: my_err1_error 

ध्यान रखें कि यह नेस्टेड फ़ंक्शन का उपयोग कर रहा है और __COUNTER__। यदि आप gcc का उपयोग कर रहे हैं तो आप सुरक्षित पक्ष में होंगे।


1

Redis गोटो का उपयोग करने की कोशिश / पकड़ने के लिए, IMHO यह बहुत साफ और सुंदर है:

/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
    char tmpfile[256];
    FILE *fp;
    rio rdb;
    int error = 0;

    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
    fp = fopen(tmpfile,"w");
    if (!fp) {
        redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
            strerror(errno));
        return REDIS_ERR;
    }

    rioInitWithFile(&rdb,fp);
    if (rdbSaveRio(&rdb,&error) == REDIS_ERR) {
        errno = error;
        goto werr;
    }

    /* Make sure data will not remain on the OS's output buffers */
    if (fflush(fp) == EOF) goto werr;
    if (fsync(fileno(fp)) == -1) goto werr;
    if (fclose(fp) == EOF) goto werr;

    /* Use RENAME to make sure the DB file is changed atomically only
     * if the generate DB file is ok. */
    if (rename(tmpfile,filename) == -1) {
        redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
        unlink(tmpfile);
        return REDIS_ERR;
    }
    redisLog(REDIS_NOTICE,"DB saved on disk");
    server.dirty = 0;
    server.lastsave = time(NULL);
    server.lastbgsave_status = REDIS_OK;
    return REDIS_OK;

werr:
    fclose(fp);
    unlink(tmpfile);
    redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
    return REDIS_ERR;
}

कोड टूट गया है। errnoकेवल असफल सिस्टम कॉल के बाद और तीन कॉल के बाद ही उपयोग किया जाना चाहिए।
14

यह कोड कई स्थानों पर लॉजिक हैंडलिंग एरर को डुप्लिकेट करता है और कई बार गलत तरीके से fclose (fp) कॉल करने जैसा काम कर सकता है। कई लेबलों का उपयोग करना बेहतर होगा और उन लेबलों का उपयोग करके जो अभी भी पुनर्प्राप्त किए जाने की आवश्यकता है उसे एनकोड करें (बजाय सभी त्रुटियों के लिए एक) और फिर कोड में त्रुटि होने पर निर्भर करते हुए सही त्रुटि हैंडलिंग स्पॉट में कूदें।
jschultz410

1

C में, आप स्पष्ट त्रुटि हैंडलिंग के लिए if + goto के मैन्युअल उपयोग के माध्यम से स्वचालित "ऑब्जेक्ट रिक्लेमेशन" के साथ अपवादों का "अनुकरण" कर सकते हैं।

मैं अक्सर निम्नलिखित की तरह सी कोड लिखता हूं (त्रुटि हैंडलिंग को उजागर करने के लिए उबला हुआ):

#include <assert.h>

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    if ( ( ret = foo_init( f ) ) )
        goto FAIL;

    if ( ( ret = goo_init( g ) ) )
        goto FAIL_F;

    if ( ( ret = poo_init( p ) ) )
        goto FAIL_G;

    if ( ( ret = loo_init( l ) ) )
        goto FAIL_P;

    assert( 0 == ret );
    goto END;

    /* error handling and return */

    /* Note that we finalize in opposite order of initialization because we are unwinding a *STACK* of initialized objects */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

यह पूरी तरह से मानक एएनएसआई सी है, अपने मेनलाइन कोड से दूर होने वाली त्रुटि को अलग करता है, प्रारंभिक वस्तुओं की सीडिंग करता है जैसे कि सी ++ करता है, और यह पूरी तरह से स्पष्ट है कि यहां क्या हो रहा है। क्योंकि आप प्रत्येक बिंदु पर विफलता के लिए स्पष्ट रूप से परीक्षण कर रहे हैं, जिससे विशिष्ट लॉगिंग या त्रुटि से निपटने में प्रत्येक स्थान पर त्रुटि डालना आसान हो सकता है।

यदि आप थोड़ा मैक्रो जादू नहीं करते हैं, तो आप स्टैक के निशान के साथ लॉगिंग त्रुटियों जैसी अन्य चीजों को करते हुए इसे और अधिक संक्षिप्त बना सकते हैं। उदाहरण के लिए:

#include <assert.h>
#include <stdio.h>
#include <string.h>

#define TRY( X, LABEL ) do { if ( ( X ) ) { fprintf( stderr, "%s:%d: Statement '" #X "' failed! %d, %s\n", __FILE__, __LINE__, ret, strerror( ret ) ); goto LABEL; } while ( 0 )

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    TRY( ret = foo_init( f ), FAIL );
    TRY( ret = goo_init( g ), FAIL_F );
    TRY( ret = poo_init( p ), FAIL_G );
    TRY( ret = loo_init( l ), FAIL_P );

    assert( 0 == ret );
    goto END;

    /* error handling and return */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

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

यह भी केवल एक ही कार्य के भीतर काम करता है और जब तक उच्च स्तर के कॉलर्स समान स्पष्ट त्रुटि वाले लॉजिक को लागू नहीं करते, तब तक स्टैक को जंप नहीं करते रहेंगे, जबकि C ++ अपवाद केवल स्टैक को ऊपर कूदने तक रहेगा, जब तक कि यह एक उपयुक्त हैंडलर न मिल जाए। न ही यह आपको एक मनमाना प्रकार फेंकने की अनुमति देता है, बल्कि केवल एक त्रुटि कोड है।

व्यवस्थित रूप से इस तरह से कोडिंग करना (यानी - एकल प्रविष्टि और एकल निकास बिंदु के साथ) पूर्व और पोस्ट ("अंत") तर्क को सम्मिलित करना बहुत आसान बनाता है जो कि किसी भी मामले को निष्पादित नहीं करेगा। आपने END लेबल के बाद अपना "अंततः" तर्क दिया।


1
बहुत अच्छा। मैं भी कुछ ऐसा ही करता हूं। गोटो इस परिदृश्य के लिए बहुत अच्छा है। एकमात्र अंतर यह है कि मुझे उस अंतिम "गेटो END" की आवश्यकता नहीं दिखती है, मैं बस उस बिंदु पर एक सफलता वापसी सम्मिलित करता हूं, बाकी के बाद एक असफल वापसी।
नील रॉय

1
धन्यवाद @NeilRoy गोटो END का कारण यह है कि मुझे एक प्रविष्टि बिंदु और एकल निकास बिंदु के लिए मेरे अधिकांश कार्य पसंद हैं। इस तरह अगर मैं किसी भी फ़ंक्शन में कुछ "अंत में" तर्क जोड़ना चाहता हूं, तो मुझे आसानी से चिंता करने की आवश्यकता के बिना कुछ और छिपा हुआ रिटर्न मिल सकता है। :)
jschultz410

0

यदि आप Win32 के साथ C का उपयोग कर रहे हैं, तो आप कोशिश / कैच को अनुकरण करने के लिए इसके संरचित अपवाद हैंडलिंग (SEH) का लाभ उठा सकते हैं ।

यदि आप उन प्लेटफ़ॉर्म में C का उपयोग कर रहे हैं जो समर्थन नहीं करते हैं setjmp()और longjmp(), pjsip लाइब्रेरी की इस अपवाद हैंडलिंग पर एक नज़र है, तो यह इसका सही कार्यान्वयन प्रदान नहीं करता है


-1

शायद एक प्रमुख भाषा (दुर्भाग्य से) नहीं है, लेकिन एपीएल में, (EA ऑपरेशन (एक्सक्यूट ऑल्टरनेट के लिए स्टैंड) का इलाज करता है।

उपयोग: 'Y''EA 'X' जहां X और Y या तो कोड स्निपेट हैं जिन्हें स्ट्रिंग्स या फ़ंक्शन नामों के रूप में आपूर्ति की जाती है।

यदि X एक त्रुटि में चलता है, तो इसके बजाय Y (आमतौर पर त्रुटि से निपटने) को निष्पादित किया जाएगा।


2
हाय mappo, StackOverflow में आपका स्वागत है। जबकि दिलचस्प, सवाल विशेष रूप से सी में ऐसा करने के बारे में था। इसलिए यह वास्तव में इस सवाल का जवाब
लूज़र
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.