एपीआई डिजाइन सी में नुकसान [बंद]


10

सी एपीआई (मानक पुस्तकालयों, तीसरे पक्ष के पुस्तकालयों, और एक परियोजना के अंदर हेडर सहित) में आपको क्या खामियां हैं जो आपको ड्राइव करते हैं? लक्ष्य सी में एपीआई डिजाइन नुकसान की पहचान करना है, इसलिए नए सी लाइब्रेरी लिखने वाले लोग अतीत की गलतियों से सीख सकते हैं।

स्पष्ट करें कि दोष क्यों खराब है (अधिमानतः एक उदाहरण के साथ), और सुधार का सुझाव देने का प्रयास करें। यद्यपि आपका समाधान वास्तविक जीवन में व्यावहारिक नहीं हो सकता है (इसे ठीक करने के लिए बहुत देर हो चुकी है strncpy), इसे भविष्य के पुस्तकालय-लेखकों के लिए सिर देना चाहिए।

हालांकि इस सवाल का फोकस सी एपीआई है, जो समस्याएं उन्हें अन्य भाषाओं में उपयोग करने की आपकी क्षमता को प्रभावित करती हैं उनका स्वागत है।

कृपया प्रति उत्तर में एक दोष दें, ताकि लोकतंत्र जवाबों को छाँट सके।


3
जॉय, यह सवाल उन लोगों की सूची बनाने के लिए रचनात्मक नहीं है जो घृणा करते हैं। प्रश्न के उपयोगी होने के लिए यहां संभावनाएं हैं यदि उत्तर बताते हैं कि वे जिन प्रथाओं को इंगित कर रहे हैं वे खराब क्यों हैं और उन्हें सुधारने के बारे में विस्तृत जानकारी प्रदान करें। उस अंत तक, कृपया अपने उदाहरण को प्रश्न के उत्तर से अपने स्वयं के उत्तर में ले जाएं और समझाएं कि यह समस्या क्यों है / एक malloc'd' स्ट्रिंग इसे कैसे ठीक करेगा। मुझे लगता है कि पहले उत्तर के साथ एक अच्छा उदाहरण स्थापित करना वास्तव में इस प्रश्न को पनपने में मदद कर सकता है। धन्यवाद!
एडम लीयर

1
@ एना लियर: मुझे यह बताने के लिए धन्यवाद कि मेरा प्रश्न समस्याग्रस्त क्यों था। मैं एक उदाहरण और सुझाए गए विकल्प के माध्यम से इसे रचनात्मक रखने की कोशिश कर रहा था। मुझे लगता है कि मेरे मन में जो कुछ था, उसे इंगित करने के लिए मुझे वास्तव में कुछ उदाहरणों की आवश्यकता थी।
जॉय एडम्स

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

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

जवाबों:


5

असंगत या अतार्किक रिटर्न मूल्यों के साथ कार्य। दो अच्छे उदाहरण:

1) कुछ विंडो फ़ंक्शंस जो एक त्रुटि के लिए एक HANDLE का उपयोग करते हैं NULL / 0 (CreateThread), कुछ एक त्रुटि के लिए INVALID_HANDLE_VALUE / -1 का उपयोग करते हैं (CreateFile)।

2) POSIX 'टाइम' फंक्शन रिटर्न '(time_t) -1' एरर पर, जो कि 'टाइम_ टी' के बाद से वास्तव में अतार्किक है, एक हस्ताक्षरित या अहस्ताक्षरित प्रकार हो सकता है।


2
दरअसल, time_t (आमतौर पर) हस्ताक्षरित है। हालांकि, 31 दिसंबर, 1969 को "अमान्य" कहा जाना अतार्किक है। मुझे लगता है कि 60 के दशक मोटे तौर पर थे :-) गंभीरता में, एक त्रुटि कोड को वापस करने के लिए एक समाधान होगा, और एक सूचक के माध्यम से परिणाम पारित करें, जैसे: int time(time_t *out);और BOOL CreateFile(LPCTSTR lpFileName, ..., HANDLE *out);
जॉय एडम्स

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

4

गैर-वर्णनात्मक या सकारात्मक रूप से भ्रमित करने वाले नामों के साथ कार्य या पैरामीटर। उदाहरण के लिए:

1) CreateFile, विंडोज एपीआई में, वास्तव में एक फाइल नहीं बनाता है, यह एक फ़ाइल हैंडल बनाता है। यह एक फ़ाइल बना सकता है, जैसे 'ओपन' कैन, अगर किसी पैरामीटर के माध्यम से पूछा जाए। इस पैरामीटर में 'CREATE_ALWAYS' और 'CREATE_NEW' नाम के मान हैं, जिनके नाम उनके शब्दार्थ में भी संकेत नहीं देते हैं। (क्या "CREATE_ALWAYS 'का अर्थ यह है कि यदि फ़ाइल मौजूद है तो यह विफल हो जाता है? या क्या यह शीर्ष पर एक नई फ़ाइल बनाता है? क्या' CREATE_NEW 'का अर्थ है कि यह हमेशा एक नई फ़ाइल बनाता है और यदि फ़ाइल पहले से मौजूद है या विफल हो जाती है? या क्या यह एक नई फ़ाइल बनाती है?" इसके ऊपर फ़ाइल करें?)

2) POSIX pthreads API में pthread_cond_wait, जो इसके नाम के बावजूद, बिना शर्त प्रतीक्षा है।


1
इस कंडोम का pthread_cond_waitमतलब "सशर्त प्रतीक्षा" नहीं है। यह इस तथ्य को संदर्भित करता है कि आप एक स्थिति चर पर प्रतीक्षा कर रहे हैं ।
जोनाथन रेइनहर्ट

ठीक है, यह एक शर्त के लिए बिना शर्त प्रतीक्षा है ।
डेविड श्वार्ट्ज

3

हटाए गए प्रकार जो इंटरफ़ेस के माध्यम से हटाए गए हैंडल के रूप में पारित किए जाते हैं। बेशक, समस्या यह है कि कंपाइलर सही तर्क प्रकारों के लिए उपयोगकर्ता कोड की जांच नहीं कर सकता है।

यह विभिन्न रूपों और स्वादों में आता है, जिसमें शामिल हैं, लेकिन इन तक सीमित नहीं है:

  • void* गाली

  • intसंसाधन संभाल के रूप में उपयोग करना (उदाहरण: CDI लाइब्रेरी)

  • कड़े टाइप किए गए तर्क

अधिक विशिष्ट प्रकार (= पूरी तरह से परस्पर विनिमय के लिए इस्तेमाल नहीं किया जा सकता) को उसी प्रकार के हटाए गए प्रकार से मैप किया जाता है, जो बदतर है। बेशक, यह उपाय केवल (सी उदाहरण) की तर्ज पर टाइपस अपारदर्शी संकेत प्रदान करने के लिए है:

typedef struct Foo Foo;
typedef struct Bar Bar;

Foo* createFoo();
Bar* createBar();

int doSomething(Foo* foo);
void somethingElse(Foo* foo, Bar* bar);

void destroyFoo(Foo* foo);
void destroyBar(Bar* bar);

2

असंगत और अक्सर बोझिल स्ट्रिंग वापसी सम्मेलनों के साथ कार्य।

उदाहरण के लिए, getcwd एक उपयोगकर्ता द्वारा आपूर्ति किए गए बफर और उसके आकार के लिए पूछता है। इसका मतलब है कि किसी एप्लिकेशन को या तो वर्तमान निर्देशिका लंबाई पर एक मनमानी सीमा निर्धारित करनी है, या ऐसा कुछ करना है ( CCAN से ):

 /* *This* is why people hate C. */
len = 32;
cwd = talloc_array(ctx, char, len);
while (!getcwd(cwd, len)) {
    if (errno != ERANGE) {
        talloc_free(cwd);
        return NULL;
    }
    cwd = talloc_realloc(ctx, cwd, char, len *= 2);
}

मेरा समाधान: एक mallocएड स्ट्रिंग लौटाएं । यह सरल, मजबूत और कम कुशल नहीं है। एम्बेडेड प्लेटफार्मों और पुराने सिस्टम को छोड़कर, mallocवास्तव में काफी तेज है।


4
मैं इस बुरे अभ्यास को नहीं कहूंगा, मैं इस अच्छे अभ्यास को कहूंगा। 1) यह पूरी तरह से सामान्य है कि कोई भी प्रोग्रामर इससे आश्चर्यचकित न हो। 2) यह कॉलर को आवंटन छोड़ देता है, जो मेमोरी लीक बग की कई संभावनाओं को बाहर करता है। 3) यह सांख्यिकीय रूप से आवंटित बफ़र्स के साथ संगत है। 4) यह फ़ंक्शन कार्यान्वयन क्लीनर बनाता है, कुछ गणितीय सूत्र की गणना करने वाले फ़ंक्शन को पूरी तरह से असंबंधित कुछ के साथ संबंधित नहीं होना चाहिए जैसे कि गतिशील मेमोरी आवंटन। आपको लगता है कि मुख्य क्लीनर हो जाता है, लेकिन फ़ंक्शन गड़बड़ हो जाता है। 5) कई सिस्टम पर मॉलॉक की अनुमति भी नहीं है।

@ लुंडिन: समस्या यह है, यह प्रोग्रामर को अनावश्यक हार्ड-कोडेड सीमाएं बनाने की ओर ले जाता है, और उन्हें वास्तव में कठिन प्रयास करना पड़ता है न कि (ऊपर उदाहरण देखें)। यह उन चीजों के लिए ठीक है snprintf(buf, 32, "%d", n), जहां आउटपुट लंबाई अनुमानित है (निश्चित रूप से 30 से अधिक नहीं, जब तक intकि आपके सिस्टम पर वास्तव में बहुत बड़ा न हो )। वास्तव में, मॉलोक कई प्रणालियों पर उपलब्ध नहीं है, लेकिन डेस्कटॉप और सर्वर वातावरण के लिए, यह है, और यह वास्तव में अच्छी तरह से काम करता है।
जॉय एडम्स

लेकिन समस्या यह है कि आपके उदाहरण में फ़ंक्शन कोई हार्ड-कोडित सीमा निर्धारित करता है। इस तरह कोड आम बात नहीं है। यहां, मुख्य बफर लंबाई के बारे में चीजें जानता है जो फ़ंक्शन को पता होना चाहिए। यह सब खराब प्रोग्राम डिजाइन का सुझाव देता है। मुख्य को यह पता नहीं लगता है कि getcwd फ़ंक्शन क्या करता है, इसलिए यह पता लगाने के लिए कुछ "जानवर बल" आवंटन का उपयोग कर रहा है। कहीं न कहीं उस मॉड्यूल के बीच का इंटरफेस जिसमें गेटकवर्ड रहता है और कॉल करने वाले को मैल्ड किया जाता है। इसका मतलब यह नहीं है कि कॉलिंग फ़ंक्शन का यह तरीका खराब है, इसके विपरीत अनुभव से पता चलता है कि यह उन कारणों के लिए अच्छा है जो मैंने पहले ही सूचीबद्ध किए हैं।

1

ऐसे कंपाउंड जो डेटा कंपाउंड प्रकारों को वैल्यू द्वारा लेते हैं, या जो कॉलबैक का उपयोग करते हैं।

इससे भी बदतर अगर कहा जाता है कि प्रकार एक संघ है या इसमें बिट-फ़ील्ड हैं।

C कॉलर के दृष्टिकोण से, ये वास्तव में ठीक हैं, लेकिन मैं C या C ++ में तब तक नहीं लिखता जब तक कि आवश्यक न हो, इसलिए मैं आमतौर पर एक FFI के माध्यम से कॉल कर रहा हूं। अधिकांश एफएफआई यूनियनों या बिट-फील्ड्स का समर्थन नहीं करते हैं, और कुछ (जैसे हास्केल और एमएलटन) मूल्य के लिए पारित किए गए ढांचे का समर्थन नहीं कर सकते हैं। उन लोगों के लिए जो मूल्य-संरचना को संभाल सकते हैं, कम से कम कॉमन लिस्प और LJJIT को धीमे रास्तों पर मजबूर किया जाता है - लिस्प के कॉमन फॉरेन फंक्शन इंटरफ़ेस को लिबफी के माध्यम से धीमी गति से कॉल करना चाहिए, और LuaJIT कॉल करने वाले कोड पथ को JIT- संकलित करने से इनकार करता है। होस्ट जो कॉल बैक कर सकते हैं, वे LuaJIT, जावा और हास्केल पर धीमी गति से ट्रिगर करते हैं, साथ ही LuaJIT ऐसी कॉल को संकलित करने में सक्षम नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.