C में सेटजम्प और लॉन्गजम्प का व्यावहारिक उपयोग


97

क्या कोई मुझे समझा सकता है कि वास्तव में setjmp()और longjmp()कार्यों को एम्बेडेड प्रोग्रामिंग में व्यावहारिक रूप से कैसे इस्तेमाल किया जा सकता है? मुझे पता है कि ये एरर हैंडलिंग के लिए हैं। लेकिन मैं कुछ उपयोग मामलों को जानना चाहूंगा।


किसी अन्य प्रोग्रामिंग की तरह त्रुटि से निपटने के लिए। मैं उपयोग में अंतर नहीं देखता हूँ ???
टोनी द लॉयन


गति के लिए? हाँ। क्योंकि a) यह लूप की तुलना में धीमा चलता है, और b) क्योंकि इसे आसानी से ऑप्टिमाइज़ नहीं किया जा सकता है (जैसे देरी हटाना, या दो)। तो setjmp और longjmp स्पष्ट रूप से शासन!
TheBlastOne

दिए गए लोगों की तुलना में एक और जवाब यहाँ है stackoverflow.com/questions/7334595/… आप longjmp()एक सिग्नल हैंडलर से बाहर निकलने के लिए उपयोग कर सकते हैं , विशेष रूप से एक जैसी चीजें BUS ERROR। यह संकेत आमतौर पर पुनरारंभ नहीं हो सकता है। एक एम्बेडेड एप्लिकेशन सुरक्षा और मजबूत संचालन के लिए इस मामले को संभालने की इच्छा कर सकता है।
आर्टलेस शोर

और setjmpबीएसडी और लिनक्स के बीच प्रदर्शन के अंतर के बारे में , "टाइमिंग सेटजम्प, और जोय ऑफ स्टैंडर्ड्स" देखें , जो उपयोग करने का सुझाव देता है sigsetjmp
Ioannis Filippidis

जवाबों:


82

त्रुटि निवारण
मान लीजिए गहरी एक समारोह में कई अन्य कार्यों और त्रुटि हैंडलिंग बनाता में नेस्ट में नीचे भावना केवल शीर्ष स्तर समारोह में कोई त्रुटि है।

यह बहुत थकाऊ और अजीब होगा अगर बीच के सभी कार्यों को सामान्य रूप से वापस करना पड़ा और रिटर्न मान या एक वैश्विक त्रुटि चर का मूल्यांकन करना था ताकि यह निर्धारित किया जा सके कि आगे की प्रक्रिया समझ में नहीं आती है या खराब भी होगी।

यह एक ऐसी स्थिति है जहाँ setjmp / longjmp समझ में आता है। वे परिस्थितियां ऐसी स्थिति के समान हैं जहां अन्य लैंगेज (सी ++, जावा) में अपवाद का अर्थ है।

Coroutines
त्रुटि से निपटने के अलावा, मैं एक और स्थिति के बारे में भी सोच सकता हूँ जहाँ आपको C में सेटजम्प / longjmp की आवश्यकता है:

यह मामला है जब आपको कोरटाइन लागू करने की आवश्यकता होती है ।

यहाँ थोड़ा डेमो उदाहरण है। मुझे उम्मीद है कि यह कुछ उदाहरण कोड के लिए शिवप्रसाद पलास के अनुरोध को संतुष्ट करता है और TheBlastOne के सवाल का जवाब देता है कि कैसे सेटजम्प / लॉन्गजम्प कॉरआउट्स के कार्यान्वयन का समर्थन करता है (जितना मैं देखता हूं कि यह किसी भी गैर-मानक या नए व्यवहार के आधार नहीं है)।

संपादित करें:
यह हो सकता है कि कॉलस्टैक को कम करने के लिए यह वास्तव में अपरिभाषित व्यवहार है (माइकएमबी की टिप्पणी देखें; हालांकि मुझे अभी तक इसे सत्यापित करने का अवसर नहीं मिला है)।longjmp

#include <stdio.h>
#include <setjmp.h>

jmp_buf bufferA, bufferB;

void routineB(); // forward declaration 

void routineA()
{
    int r ;

    printf("(A1)\n");

    r = setjmp(bufferA);
    if (r == 0) routineB();

    printf("(A2) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20001);

    printf("(A3) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20002);

    printf("(A4) r=%d\n",r);
}

void routineB()
{
    int r;

    printf("(B1)\n");

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10001);

    printf("(B2) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10002);

    printf("(B3) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10003);
}


int main(int argc, char **argv) 
{
    routineA();
    return 0;
}

निम्नलिखित आंकड़ा निष्पादन के प्रवाह को दर्शाता है:
निष्पादन का प्रवाह

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


2
चूँकि सेटजम्प तैयार करता है, और लोंगजम्प वर्तमान कॉल स्कोप से जंप को सेटजम्प स्कोप में वापस करता है, तो यह कोरटाइन के कार्यान्वयन का समर्थन कैसे करेगा? मैं यह नहीं देखता कि कोई उस रुटीन के निष्पादन को कैसे जारी रख सकता है जो longjmp outd से बाहर हो।
TheBlastOne

2
@ TheBlastOne विकिपीडिया लेख देखें । यदि आप setjmpसे पहले आप निष्पादन जारी रख सकते हैं longjmp। यह अमानवीय है।
पोटाटोसवेटर

9
Coroutines को अलग-अलग स्टाॅक पर चलाने की आवश्यकता होती है, न कि उसी तरह, जैसा आपके उदाहरण में दिखाया गया है। के रूप में routineAऔर routineBएक ही ढेर का उपयोग करें, यह केवल बहुत आदिम coroutines के लिए काम करता है। यदि पहली कॉल के बाद routineAएक गहरी नेस्टेड routineCकॉल किया जाता है routineBऔर यह कोरटाइन के रूप में routineCचलता है routineB, तो routineBहो सकता है कि रिटर्न स्टैक (न केवल स्थानीय चर) को नष्ट कर दे routineC। तो एक विशेष स्टैक ( alloca()कॉल करने के बाद के माध्यम से rountineB?) को आवंटित किए बिना आप इस उदाहरण के साथ गंभीर परेशानी में पड़ जाएंगे यदि नुस्खा के रूप में उपयोग किया जाता है।
तिनो

6
कृपया उल्लेख करें, आपके उत्तर में कि कॉलस्टैक (ए से बी तक) नीचे कूदना अपरिभाषित व्यवहार है)।
माइकएमबी

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

18

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

हर चतुर सिद्धांत की तरह यह वास्तविकता से मिलते समय अलग हो जाता है। आपके मध्यवर्ती कार्य मेमोरी को आवंटित करेंगे, ताले को पकड़ेंगे, फाइलें खोलेंगे और सभी प्रकार की विभिन्न चीजें करेंगे जो सफाई की आवश्यकता होती है। तो व्यवहार में setjmp/ longjmpआमतौर पर बहुत ही सीमित परिस्थितियों को छोड़कर एक बुरा विचार है जहां आपका अपने पर्यावरण (कुछ एम्बेडेड प्लेटफार्मों) पर कुल नियंत्रण है।

ज्यादातर मामलों में मेरे अनुभव में जब भी आपको लगता है कि प्रयोग setjmp/ longjmpकाम करेंगे, तो आपका कार्यक्रम स्पष्ट और सरल है कि कॉल श्रृंखला में प्रत्येक मध्यवर्ती फ़ंक्शन कॉल त्रुटि से निपटने में कर सकता है, या इसे ठीक करने के लिए इतना गन्दा और असंभव है कि आपको exitकब करना चाहिए त्रुटि का सामना करना।


3
कृपया देखिए libjpeg। C ++ की तरह, C रूटीन के अधिकांश संग्रह सामूहिक के struct *रूप में किसी चीज़ को संचालित करने के लिए लेते हैं । स्थानीय लोगों के रूप में आपके मध्यवर्ती फ़ंक्शंस मेमोरी आवंटन को संग्रहीत करने के बजाय, उन्हें संरचना में संग्रहीत किया जा सकता है। यह एक longjmp()हैंडलर को मेमोरी को खाली करने की अनुमति देता है । इसके अलावा, इसमें इतने सारे ब्लास्ट अपवाद सारणी नहीं हैं कि सभी C ++ कंपाइलर तथ्य के 20 साल बाद भी उत्पन्न होते हैं।
आर्टलेस शोर

Like every clever theory this falls apart when meeting reality.वास्तव में, अस्थायी आवंटन और इसी तरह longjmp()से मुश्किल हो जाता है, क्योंकि आपके पास setjmp()कॉल स्टैक में कई बार होता है (एक बार प्रत्येक फ़ंक्शन के लिए जिसे बाहर निकलने से पहले किसी प्रकार की सफाई करने की आवश्यकता होती है, जिसे फिर "अपवाद को फिर से बढ़ाने" की आवश्यकता होती है) द्वारा longjmp()संदर्भ के लिए ing इसे शुरू में प्राप्त किया था कि)। यह और भी बदतर हो जाता है अगर उन संसाधनों को संशोधित किया जाता है setjmp(), क्योंकि आपको उन्हें घोषित करना volatileहोगा ताकि longjmp()उन्हें क्लॉबरिंग से रोका जा सके ।
सेवको

10

के संयोजन setjmpऔर longjmp"सुपर शक्ति है goto"। अत्यधिक देखभाल के साथ प्रयोग करें। हालाँकि, जैसा कि अन्य लोगों ने समझाया longjmpहै, जब आप get me back to the beginningजल्दी से जल्दी काम करना चाहते हैं , तो यह 18 परतों के कार्यों के लिए एक त्रुटि संदेश को वापस लाने के बजाय, एक त्रुटि त्रुटि स्थिति से बाहर निकलने के लिए बहुत उपयोगी है ।

हालाँकि, बस goto, लेकिन इससे भी बदतर, आपको वास्तव में सावधान रहना होगा कि आप इसका उपयोग कैसे करते हैं। A longjmpबस आपको कोड की शुरुआत में वापस लाएगा। यह उन सभी अन्य राज्यों को प्रभावित नहीं करेगा, जो शुरू setjmpहोने और फिर से setjmpशुरू होने के बीच बदल सकते हैं । इसलिए, आवंटन, ताले, आधा-आरंभिक डेटा संरचनाएं, आदि, अभी भी आवंटित किए गए हैं, बंद और आधे-प्रारंभिक किए गए हैं जब आप वापस उसी स्थान पर पहुंच जाते हैं जहां setjmpबुलाया गया था। इसका मतलब है, आपको वास्तव में उन स्थानों की देखभाल करनी होगी जहां आप ऐसा करते हैं, कि longjmpअधिक समस्याओं के कारण कॉल करना वास्तव में ठीक है। बेशक, अगर आप जो अगला काम करते हैं, वह "रिबूट" है [त्रुटि के बारे में एक संदेश को संग्रहीत करने के बाद, शायद] - एक एम्बेडेड सिस्टम में जहां आपने पता लगाया है कि हार्डवेयर खराब स्थिति में है, उदाहरण के लिए, तो ठीक है।

मैंने बहुत बुनियादी सूत्रण तंत्र प्रदान करने के लिए भी देखा setjmp/ longjmpउपयोग किया है। लेकिन यह बहुत खास मामला है - और निश्चित रूप से नहीं "मानक" धागे कैसे काम करते हैं।

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

struct 
{
    void (*destructor)(void *ptr);
};


void LockForceUnlock(void *vlock)
{
   LOCK* lock = vlock;
}


LOCK func_lock;


void func()
{
   ref = add_destructor(LockForceUnlock, mylock);
   Lock(func_lock)
   ... 
   func2();   // May call longjmp. 

   Unlock(func_lock);
   remove_destructor(ref);
}

इस प्रणाली के साथ, आप "C ++ की तरह पूर्ण अपवाद हैंडलिंग" कर सकते हैं। लेकिन यह काफी गड़बड़ है, और अच्छी तरह से लिखे जा रहे कोड पर निर्भर करता है।


+1, निश्चित रूप से आप सिद्धांत में setjmpप्रत्येक इनिशियलाइज़ेशन, एक ला C ++ ... और इसके लायक है कि थ्रेडिंग के लिए इसका उपयोग करने के लायक नहीं है, को ध्यान में रखते हुए कॉल करके स्वच्छ अपवाद हैंडलिंग को लागू कर सकते हैं ।
पोटाटोसवाटर

8

चूंकि आप एम्बेडेड उल्लेख करते हैं, मुझे लगता है कि यह एक गैर-उपयोग के मामले में ध्यान देने योग्य है : जब आपका कोडिंग मानक इसे प्रतिबंधित करता है। उदाहरण के लिए MISRA (MISRA-C: 2004: नियम 20.7) और JFS (AV नियम 20): "सेटजम्प मैक्रो और लॉन्जम्प फ़ंक्शन का उपयोग नहीं किया जाएगा।"


8

setjmpऔर longjmpइकाई परीक्षण में बहुत उपयोगी हो सकता है।

मान लें कि हम निम्नलिखित मॉड्यूल का परीक्षण करना चाहते हैं:

#include <stdlib.h>

int my_div(int x, int y)
{
    if (y==0) exit(2);
    return x/y;
}

आम तौर पर, यदि फ़ंक्शन परीक्षण करने के लिए किसी अन्य फ़ंक्शन को कॉल करता है, तो आप कॉल करने के लिए इसके लिए एक स्टब फ़ंक्शन की घोषणा कर सकते हैं जो कुछ प्रवाह का परीक्षण करने के लिए वास्तविक फ़ंक्शन की नकल करेगा। हालांकि इस मामले में, फ़ंक्शन कॉल exitकरता है जो वापस नहीं आता है। ठूंठ को किसी तरह इस व्यवहार का अनुकरण करने की आवश्यकता है। setjmpऔर longjmpआप के लिए ऐसा कर सकते हैं।

इस फ़ंक्शन का परीक्षण करने के लिए, हम निम्नलिखित परीक्षण कार्यक्रम बना सकते हैं:

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

// redefine assert to set a boolean flag
#ifdef assert
#undef assert
#endif
#define assert(x) (rslt = rslt && (x))

// the function to test
int my_div(int x, int y);

// main result return code used by redefined assert
static int rslt;

// variables controling stub functions
static int expected_code;
static int should_exit;
static jmp_buf jump_env;

// test suite main variables
static int done;
static int num_tests;
static int tests_passed;

//  utility function
void TestStart(char *name)
{
    num_tests++;
    rslt = 1;
    printf("-- Testing %s ... ",name);
}

//  utility function
void TestEnd()
{
    if (rslt) tests_passed++;
    printf("%s\n", rslt ? "success" : "fail");
}

// stub function
void exit(int code)
{
    if (!done)
    {
        assert(should_exit==1);
        assert(expected_code==code);
        longjmp(jump_env, 1);
    }
    else
    {
        _exit(code);
    }
}

// test case
void test_normal()
{
    int jmp_rval;
    int r;

    TestStart("test_normal");
    should_exit = 0;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(12,3);
    }

    assert(jmp_rval==0);
    assert(r==4);
    TestEnd();
}

// test case
void test_div0()
{
    int jmp_rval;
    int r;

    TestStart("test_div0");
    should_exit = 1;
    expected_code = 2;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(2,0);
    }

    assert(jmp_rval==1);
    TestEnd();
}

int main()
{
    num_tests = 0;
    tests_passed = 0;
    done = 0;
    test_normal();
    test_div0();
    printf("Total tests passed: %d\n", tests_passed);
    done = 1;
    return !(tests_passed == num_tests);
}

इस उदाहरण में, आप setjmpपरीक्षण करने के लिए फ़ंक्शन में प्रवेश करने से पहले उपयोग करते हैं , फिर स्टबड में exitआप longjmpसीधे अपने परीक्षण के मामले में वापस आने के लिए कॉल करते हैं।

यह भी ध्यान दें कि पुनर्निर्धारित exitएक विशेष चर है जिसे यह देखने के लिए जांचता है कि क्या आप वास्तव में प्रोग्राम से बाहर निकलना चाहते हैं और _exitऐसा करने के लिए कॉल करते हैं। यदि आप ऐसा नहीं करते हैं, तो आपका परीक्षा कार्यक्रम साफ-सुथरा नहीं रह सकता है।


6

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

यहाँ एक नमूना है कि यह कैसे कोड का उपयोग कर दिखता है:

try
{
    *((int *)0) = 0;    /* may not be portable */
}
catch (SegmentationFault, e)
{
    long f[] = { 'i', 'l', 'l', 'e', 'g', 'a', 'l' };
    ((void(*)())f)();   /* may not be portable */
}
finally
{
    return(1 / strcmp("", ""));
}

और यहाँ शामिल फ़ाइल का एक हिस्सा है जिसमें बहुत सारे तर्क हैं:

#ifndef _EXCEPT_H
#define _EXCEPT_H

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include "Lifo.h"
#include "List.h"

#define SETJMP(env)             sigsetjmp(env, 1)
#define LONGJMP(env, val)       siglongjmp(env, val)
#define JMP_BUF                 sigjmp_buf

typedef void (* Handler)(int);

typedef struct _Class *ClassRef;        /* exception class reference */
struct _Class
{
    int         notRethrown;            /* always 1 (used by throw()) */
    ClassRef    parent;                 /* parent class */
    char *      name;                   /* this class name string */
    int         signalNumber;           /* optional signal number */
};

typedef struct _Class Class[1];         /* exception class */

typedef enum _Scope                     /* exception handling scope */
{
    OUTSIDE = -1,                       /* outside any 'try' */
    INTERNAL,                           /* exception handling internal */
    TRY,                                /* in 'try' (across routine calls) */
    CATCH,                              /* in 'catch' (idem.) */
    FINALLY                             /* in 'finally' (idem.) */
} Scope;

typedef enum _State                     /* exception handling state */
{
    EMPTY,                              /* no exception occurred */
    PENDING,                            /* exception occurred but not caught */
    CAUGHT                              /* occurred exception caught */
} State;

typedef struct _Except                  /* exception handle */
{
    int         notRethrown;            /* always 0 (used by throw()) */
    State       state;                  /* current state of this handle */
    JMP_BUF     throwBuf;               /* start-'catching' destination */
    JMP_BUF     finalBuf;               /* perform-'finally' destination */
    ClassRef    class;                  /* occurred exception class */
    void *      pData;                  /* exception associated (user) data */
    char *      file;                   /* exception file name */
    int         line;                   /* exception line number */
    int         ready;                  /* macro code control flow flag */
    Scope       scope;                  /* exception handling scope */
    int         first;                  /* flag if first try in function */
    List *      checkList;              /* list used by 'catch' checking */
    char*       tryFile;                /* source file name of 'try' */
    int         tryLine;                /* source line number of 'try' */

    ClassRef    (*getClass)(void);      /* method returning class reference */
    char *      (*getMessage)(void);    /* method getting description */
    void *      (*getData)(void);       /* method getting application data */
    void        (*printTryTrace)(FILE*);/* method printing nested trace */
} Except;

typedef struct _Context                 /* exception context per thread */
{
    Except *    pEx;                    /* current exception handle */
    Lifo *      exStack;                /* exception handle stack */
    char        message[1024];          /* used by ExceptGetMessage() */
    Handler     sigAbrtHandler;         /* default SIGABRT handler */
    Handler     sigFpeHandler;          /* default SIGFPE handler */
    Handler     sigIllHandler;          /* default SIGILL handler */
    Handler     sigSegvHandler;         /* default SIGSEGV handler */
    Handler     sigBusHandler;          /* default SIGBUS handler */
} Context;

extern Context *        pC;
extern Class            Throwable;

#define except_class_declare(child, parent) extern Class child
#define except_class_define(child, parent)  Class child = { 1, parent, #child }

except_class_declare(Exception,           Throwable);
except_class_declare(OutOfMemoryError,    Exception);
except_class_declare(FailedAssertion,     Exception);
except_class_declare(RuntimeException,    Exception);
except_class_declare(AbnormalTermination, RuntimeException);  /* SIGABRT */
except_class_declare(ArithmeticException, RuntimeException);  /* SIGFPE */
except_class_declare(IllegalInstruction,  RuntimeException);  /* SIGILL */
except_class_declare(SegmentationFault,   RuntimeException);  /* SIGSEGV */
except_class_declare(BusError,            RuntimeException);  /* SIGBUS */


#ifdef  DEBUG

#define CHECKED                                                         \
        static int checked

#define CHECK_BEGIN(pC, pChecked, file, line)                           \
            ExceptCheckBegin(pC, pChecked, file, line)

#define CHECK(pC, pChecked, class, file, line)                          \
                 ExceptCheck(pC, pChecked, class, file, line)

#define CHECK_END                                                       \
            !checked

#else   /* DEBUG */

#define CHECKED
#define CHECK_BEGIN(pC, pChecked, file, line)           1
#define CHECK(pC, pChecked, class, file, line)          1
#define CHECK_END                                       0

#endif  /* DEBUG */


#define except_thread_cleanup(id)       ExceptThreadCleanup(id)

#define try                                                             \
    ExceptTry(pC, __FILE__, __LINE__);                                  \
    while (1)                                                           \
    {                                                                   \
        Context *       pTmpC = ExceptGetContext(pC);                   \
        Context *       pC = pTmpC;                                     \
        CHECKED;                                                        \
                                                                        \
        if (CHECK_BEGIN(pC, &checked, __FILE__, __LINE__) &&            \
            pC->pEx->ready && SETJMP(pC->pEx->throwBuf) == 0)           \
        {                                                               \
            pC->pEx->scope = TRY;                                       \
            do                                                          \
            {

#define catch(class, e)                                                 \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        else if (CHECK(pC, &checked, class, __FILE__, __LINE__) &&      \
                 pC->pEx->ready && ExceptCatch(pC, class))              \
        {                                                               \
            Except *e = LifoPeek(pC->exStack, 1);                       \
            pC->pEx->scope = CATCH;                                     \
            do                                                          \
            {

#define finally                                                         \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        if (CHECK_END)                                                  \
            continue;                                                   \
        if (!pC->pEx->ready && SETJMP(pC->pEx->finalBuf) == 0)          \
            pC->pEx->ready = 1;                                         \
        else                                                            \
            break;                                                      \
    }                                                                   \
    ExceptGetContext(pC)->pEx->scope = FINALLY;                         \
    while (ExceptGetContext(pC)->pEx->ready > 0 || ExceptFinally(pC))   \
        while (ExceptGetContext(pC)->pEx->ready-- > 0)

#define throw(pExceptOrClass, pData)                                    \
    ExceptThrow(pC, (ClassRef)pExceptOrClass, pData, __FILE__, __LINE__)

#define return(x)                                                       \
    {                                                                   \
        if (ExceptGetScope(pC) != OUTSIDE)                              \
        {                                                               \
            void *      pData = malloc(sizeof(JMP_BUF));                \
            ExceptGetContext(pC)->pEx->pData = pData;                   \
            if (SETJMP(*(JMP_BUF *)pData) == 0)                         \
                ExceptReturn(pC);                                       \
            else                                                        \
                free(pData);                                            \
        }                                                               \
        return x;                                                       \
    }

#define pending                                                         \
    (ExceptGetContext(pC)->pEx->state == PENDING)

extern Scope    ExceptGetScope(Context *pC);
extern Context *ExceptGetContext(Context *pC);
extern void     ExceptThreadCleanup(int threadId);
extern void     ExceptTry(Context *pC, char *file, int line);
extern void     ExceptThrow(Context *pC, void * pExceptOrClass,
                            void *pData, char *file, int line);
extern int      ExceptCatch(Context *pC, ClassRef class);
extern int      ExceptFinally(Context *pC);
extern void     ExceptReturn(Context *pC);
extern int      ExceptCheckBegin(Context *pC, int *pChecked,
                                 char *file, int line);
extern int      ExceptCheck(Context *pC, int *pChecked, ClassRef class,
                            char *file, int line);


#endif  /* _EXCEPT_H */

एक सी मॉड्यूल भी है जिसमें सिग्नल हैंडलिंग और कुछ बहीखाता पद्धति के लिए तर्क शामिल हैं।

इसे लागू करना बहुत मुश्किल था, मैं आपको बता सकता हूं और मैंने लगभग छोड़ दिया। मैंने वास्तव में इसे यथासंभव जावा के करीब बनाने के लिए धक्का दिया; मुझे यह आश्चर्यजनक लगा कि मुझे सिर्फ C के साथ कितनी दूर तक जाना पड़ा।

यदि आप रुचि रखते हैं तो मुझे एक चिल्लाओ।


1
मुझे आश्चर्य है कि यह कस्टम अपवादों के लिए वास्तविक संकलक समर्थन के बिना संभव है। लेकिन वास्तव में दिलचस्प यह है कि सिग्नल अपवादों में कैसे परिवर्तित होते हैं।
पॉल स्टेलियन

मैं एक बात पूछूंगा: उन अपवादों के बारे में जो कभी खत्म नहीं होते हैं? कैसे होगा मुख्य () निकास?
पॉल स्टेलियन

1
@PaulStelian और, यहां आपका जवाब है कि main()बिना किसी छूट के कैसे निकलेंगे। कृपया इस उत्तर को जारी रखें :-)
अर्थ-मामले

1
@PaulStelian आह, मैं देख रहा हूं कि अब आपका क्या मतलब है। रन-टाइम अपवाद जो नहीं पकड़े गए हैं मेरा मानना ​​है कि फिर से उठाया गया था ताकि सामान्य (प्लेटफ़ॉर्म आश्रित) उत्तर लागू हो। पकड़े नहीं गए कस्टम अपवादों को मुद्रित और अनदेखा किया गया था। READMEProgagation में अनुभाग देखें मैंने अपना अप्रैल 1999 का कोड GitHub पर पोस्ट किया है (संपादित उत्तर में लिंक देखें)। एक नज़र देख लो; यह दरार करने के लिए एक कठिन अखरोट था। आप जो सोचते हैं, उसे सुनकर अच्छा लगेगा।
अर्थ-मामले

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

0

हाथ नीचे, setjmp / longjmp का सबसे महत्वपूर्ण उपयोग यह है कि यह "गैर-स्थानीय गोटो कूद" कार्य करता है। गोटो कमांड (और ऐसे दुर्लभ उदाहरण हैं जहां आपको गोटो ओवर के लिए और लूप्स का उपयोग करने की आवश्यकता होगी) एक ही दायरे में सबसे अधिक उपयोग किया जाता है-सुरक्षित रूप से। यदि आप गोटो का उपयोग स्कोप (या ऑटो आवंटन के पार) में कूदने के लिए करते हैं, तो आप अपने कार्यक्रम के ढेर को भ्रष्ट कर देंगे। setjmp / longjmp जिस स्थान पर आप कूदना चाहते हैं, स्टैक जानकारी को सहेजकर इससे बचता है। फिर, जब आप कूदते हैं, तो यह स्टैक जानकारी लोड करता है। इस सुविधा के बिना, सी प्रोग्रामर को सबसे अधिक असेंबली प्रोग्रामिंग की ओर मुड़ना पड़ता है ताकि उन मुद्दों को हल किया जा सके जो केवल setjmp / longjmp हल कर सकते हैं। भगवान का शुक्र है कि यह मौजूद है। C लाइब्रेरी में सब कुछ बेहद महत्वपूर्ण है। जरूरत पड़ने पर आपको पता चल जाएगा।


"C लाइब्रेरी में सब कुछ बेहद महत्वपूर्ण है।" वहाँ सामान और सामान का एक पूरा गुच्छा है जो कभी भी अच्छा नहीं था, जैसे कि स्थान।
qwr
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.