मैं कई बार इस प्रश्नोत्तर में भाग गया, और एक अधिक व्यापक उत्तर देना चाहता था। मुझे लगता है कि इस बारे में सोचने का सबसे अच्छा तरीका है कि कॉलर को त्रुटियों को कैसे वापस किया जाए, और क्या आप लौटाते हैं।
किस तरह
किसी फ़ंक्शन से जानकारी वापस करने के 3 तरीके हैं:
- प्रतिलाभ की मात्रा
- तर्क
- बैंड में से, जिसमें गैर-स्थानीय गोटो (सेटजम्प / लॉन्गजम्प), फ़ाइल या वैश्विक स्कॉप्ड चर, फ़ाइल विकल्प आदि शामिल हैं।
प्रतिलाभ की मात्रा
आप केवल वापसी कर सकते हैं मूल्य एक ही वस्तु है, हालांकि, यह एक मनमाना परिसर हो सकता है। यहाँ एक त्रुटि वापसी समारोह का एक उदाहरण है:
enum error hold_my_beer();
रिटर्न मानों का एक लाभ यह है कि यह कम घुसपैठ की त्रुटि से निपटने के लिए कॉल को चैन करने की अनुमति देता है:
!hold_my_beer() &&
!hold_my_cigarette() &&
!hold_my_pants() ||
abort();
यह न केवल पठनीयता के बारे में है, बल्कि एक समान तरीके से ऐसे फ़ंक्शन पॉइंटर्स की एक सरणी को संसाधित करने की अनुमति भी दे सकता है।
तर्क
आप तर्कों के माध्यम से एक से अधिक ऑब्जेक्ट के माध्यम से अधिक वापस आ सकते हैं, लेकिन सबसे अच्छा अभ्यास तर्क की कुल संख्या को कम रखने का सुझाव देता है (कहते हैं, <= 4):
void look_ma(enum error *e, char *what_broke);
enum error e;
look_ma(e);
if(e == FURNITURE) {
reorder(what_broke);
} else if(e == SELF) {
tell_doctor(what_broke);
}
बैंड से बाहर
सेटजम्प () के साथ आप एक जगह को परिभाषित करते हैं और आप एक अंतर मान को कैसे संभालना चाहते हैं, और आप लॉन्गजम्प () के माध्यम से उस स्थान पर नियंत्रण स्थानांतरित करते हैं। C में setjmp और longjmp का व्यावहारिक उपयोग देखें ।
क्या
- सूचक
- कोड
- वस्तु
- वापस कॉल करें
सूचक
एक त्रुटि संकेतक आपको केवल यह बताता है कि कोई समस्या है लेकिन उक्त समस्या की प्रकृति के बारे में कुछ भी नहीं है:
struct foo *f = foo_init();
if(!f) {
/// handle the absence of foo
}
किसी फ़ंक्शन के लिए त्रुटि स्थिति को संवाद करने के लिए यह कम से कम शक्तिशाली तरीका है, हालांकि, परिपूर्ण यदि कॉलर किसी भी तरह से स्नातक की उपाधि में त्रुटि का जवाब नहीं दे सकता है।
कोड
एक त्रुटि कोड कॉलर को समस्या की प्रकृति के बारे में बताता है, और एक उपयुक्त प्रतिक्रिया (ऊपर से) के लिए अनुमति दे सकता है। यह रिटर्न मान हो सकता है, या त्रुटि तर्क के ऊपर लुक_मा () उदाहरण की तरह।
वस्तु
त्रुटि ऑब्जेक्ट के साथ, कॉलर को मनमाने ढंग से जटिल मुद्दों के बारे में सूचित किया जा सकता है। उदाहरण के लिए, एक त्रुटि कोड और एक उपयुक्त मानव पठनीय संदेश। यह कॉल करने वाले को यह भी सूचित कर सकता है कि एक संग्रह को संसाधित करते समय कई चीजें गलत हो गईं, या प्रति आइटम त्रुटि हुई:
struct collection friends;
enum error *e = malloc(c.size * sizeof(enum error));
...
ask_for_favor(friends, reason);
for(int i = 0; i < c.size; i++) {
if(reason[i] == NOT_FOUND) find(friends[i]);
}
त्रुटि सरणी को पूर्व-आबंटित करने के बजाय, आप इसे (पुनः) गतिशील रूप से आवंटित कर सकते हैं जैसा कि पाठ्यक्रम की आवश्यकता है।
वापस कॉल करें
कॉलबैक त्रुटियों को संभालने का सबसे शक्तिशाली तरीका है, जैसा कि आप फ़ंक्शन को बता सकते हैं कि कुछ गलत होने पर आप क्या व्यवहार देखना चाहेंगे। कॉलबैक तर्क को प्रत्येक फ़ंक्शन में जोड़ा जा सकता है, या यदि अनुकूलन यूआईएस इस तरह की संरचना के प्रति केवल आवश्यक है:
struct foo {
...
void (error_handler)(char *);
};
void default_error_handler(char *message) {
assert(f);
printf("%s", message);
}
void foo_set_error_handler(struct foo *f, void (*eh)(char *)) {
assert(f);
f->error_handler = eh;
}
struct foo *foo_init() {
struct foo *f = malloc(sizeof(struct foo));
foo_set_error_handler(f, default_error_handler);
return f;
}
struct foo *f = foo_init();
foo_something();
एक कॉलबैक का एक दिलचस्प लाभ यह है कि इसे कई बार लागू किया जा सकता है, या त्रुटियों के अभाव में बिल्कुल भी नहीं जिसमें खुश पथ पर कोई उपरि नहीं है।
हालांकि, नियंत्रण का उलटा है। कॉलिंग कोड कॉल किया गया था, तो कॉलिंग कोड नहीं जानता है। जैसे, यह भी एक संकेतक का उपयोग करने के लिए समझ में आ सकता है।