2009-03-09 12 views
8

मैं ऐसी लाइब्रेरी पर काम कर रहा हूं जो एकाधिक प्रोग्रामिंग वातावरण जैसे वीबी 6 और फॉक्सप्रो का समर्थन करता है। मुझे सी सम्मेलन के साथ रहना है क्योंकि यह सबसे कम आम संप्रदाय है। अब मेरे पास शैली के बारे में एक सवाल है।सी एपीआई फ़ंक्शन की शैली

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

int func(input params... char* buffer, unsigned int* buffer_size); 

इस शैली के बारे में अच्छी बात यह है कि सब कुछ, प्रोटोटाइप में शामिल है त्रुटि कोड भी शामिल है। और स्मृति आवंटन से बचा जा सकता है। समस्या यह है कि समारोह काफी verbose है। और क्योंकि buffer_size कोई भी हो सकता है, इसे लागू करने के लिए और कोड की आवश्यकता है।

char* func(input params...); 

यह शैली बफर नष्ट करने के लिए फोन करने वाले की आवश्यकता है:

एक अन्य विकल्प चार * लौटने के लिए, और शून्य लौट त्रुटि इंगित करने के लिए है। मेमोरी आवंटन आवश्यक है ताकि सर्वर प्रोग्राम मेमोरी विखंडन समस्या का सामना कर सके।

दूसरे विकल्प का एक संस्करण एक थ्रेड स्थानीय चर का उपयोग करने के लिए लौटा हुआ पॉइंटर चार * रखने के लिए है, ताकि उपयोगकर्ता को बफर को हटाने की आवश्यकता न हो।

आपको कौन सी शैली पसंद है? और कारण?

+0

बफर का प्रकार char ** नहीं होना चाहिए? इसके अलावा, आपको विकल्प में एक buffer_size क्यों चाहिए और विकल्प दो में नहीं? – mweerden

+0

वह पैरामीटर के रूप में एक प्रीलाक्टेड बफर पास करता है और कॉल किए गए फ़ंक्शन को त्रुटि टेक्स्ट से भरने की अपेक्षा करता है। – sharptooth

+0

ठीक है, लेकिन फिर buffer_size को पॉइंटर होने की आवश्यकता नहीं है, है ना? – mweerden

उत्तर

1

दूसरा संस्करण क्लीनर है।

COM IErrorInfo दूसरे दृष्टिकोण का कार्यान्वयन है। सर्वर गलत त्रुटि के विवरण सेट करने के लिए SetErrorInfo को कॉल करता है और एक त्रुटि कोड देता है। कॉलर कोड की जांच करता है और विवरण प्राप्त करने के लिए GetErrorInfo को कॉल कर सकता है। कॉलर IErrorInfo को जारी करने के लिए ज़िम्मेदार है, लेकिन पहले संस्करण में प्रत्येक कॉल के पैरामीटर को पास करना भी सुंदर नहीं है।

सर्वर प्रारंभ पर पर्याप्त स्मृति को आवंटित कर सकता है ताकि त्रुटि विवरण वापस करने के लिए निश्चित रूप से पर्याप्त स्मृति हो।

2

यदि मुझे दिखाए गए दो शैलियों के बीच चयन करना है तो मैं हर बार पहली बार जाऊंगा। दूसरी शैली आपके लाइब्रेरी के उपयोगकर्ताओं को कुछ और सोचने के लिए याद करती है, यादगार आवंटन, और कोई स्मृति को मुक्त करने के लिए भूल जाता है।

5

मैं पहली परिभाषा को प्राथमिकता दूंगा, जहां बफर और इसका आकार पारित हो जाता है। अपवाद हैं, लेकिन आम तौर पर आप जिन कार्यों को कॉल करते हैं उन्हें साफ करने की अपेक्षा नहीं करते हैं। जबकि अगर मैं स्मृति आवंटित करता हूं और इसे एक समारोह में पास करता हूं, तो मुझे पता है कि मुझे अपने बाद साफ करना है।

विभिन्न आकार के बफर को संभालना एक बड़ा सौदा नहीं होना चाहिए।

+0

यह काफी हद तक विंडोज एपीआई कैसे करता है, इसलिए यह अनुकरण करने के लिए तार्किक है। –

2

दूसरी शैली के साथ एक और मुद्दा यह है कि स्मृति आवंटित किए गए संदर्भ अलग-अलग हो सकते हैं। उदाहरण के लिए:

// your library in C 
char * foo() { 
    return malloc(100); 
} 

// my client code C++ 
char * p = foo();  // call your code 
delete p;    // natural for me to do, but ... aaargh! 

और यह समस्या का केवल एक मामूली हिस्सा है। आप कह सकते हैं कि दोनों पक्षों को malloc & मुफ्त का उपयोग करना चाहिए, लेकिन यदि वे भिन्न कंपाइलर कार्यान्वयन का उपयोग कर रहे हैं तो क्या होगा? एक ही स्थान पर होने वाले सभी आवंटन और विधियों के लिए यह बेहतर है। चाहे वह लाइब्रेरी है क्लाइंट कोड आपके ऊपर है।

1

पहला संस्करण कम प्रोग्राम प्रवण होता है जब अन्य प्रोग्रामर इसका उपयोग करते हैं।

यदि प्रोग्रामर को स्मृति को आवंटित करना होता है तो उन्हें इसे मुक्त करने की अधिक संभावना होती है। यदि एक पुस्तकालय उनके लिए स्मृति आवंटित करता है तो यह अभी तक एक और अमूर्त है और जटिलताओं का कारण बन सकता है।

1

कुछ चीजों पर विचार करना;

  • आवंटन और विध्वंस एक ही दायरे (आदर्श) पर होना चाहिए। कॉलर द्वारा प्री-आवंटित बफर में पास करना सबसे अच्छा है। कॉलर इसे बाद में सुरक्षित रूप से मुक्त कर सकता है। यह सवाल उठता है - बफर कितना बड़ा होना चाहिए? एक दृष्टिकोण जो मैंने देखा है Win32 में काफी व्यापक रूप से उपयोग किया जाता है, इनपुट बफर के रूप में NULL को पास करना है और size पैरामीटर आपको बताएगा कि आपको कितनी आवश्यकता है।

  • आप कितनी संभावित त्रुटि स्थितियों की निगरानी करते हैं? char* लौटने से त्रुटि रिपोर्टिंग की सीमा सीमित हो सकती है।

  • क्या आप पहले और पोस्ट की स्थिति को पूरा करना चाहते हैं? क्या आपका प्रोटोटाइप उसको प्रतिबिंबित करता है?

  • क्या आप कॉलर या कैली में त्रुटि जांचते हैं?

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

8

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

इसलिए, यदि आप "सबसे कम आम denominator" की तलाश में हैं, तो आप यह भी सोचना चाहेंगे कि आपके लक्षित माहौल में कौन सी भाषा संरचनाएं उपलब्ध हैं (संकलक मानक सी के भीतर कुछ भी स्वीकार करने की संभावना है, लेकिन अगर कुछ असमर्थित है लिंकर नहीं कहेंगे)।

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

त्रुटि संचार एपीआई से निपटने के दौरान सबसे महत्वपूर्ण चीजों में से एक है। चूंकि उपयोगकर्ता के पास उसके कोड में त्रुटियों को संभालने के लिए अलग-अलग तरीके हैं, इसलिए आपको पूरे एपीआई में इस संचार के बारे में जितना संभव हो सके उतना संगत होना चाहिए। उपयोगकर्ता आपके एपीआई की तरफ एक सतत तरीके से और न्यूनतम कोड के साथ त्रुटि प्रबंधन को लपेटने में सक्षम होना चाहिए। मैं आमतौर पर हमेशा स्पष्ट enum कोड या परिभाषित/typedefs का उपयोग करने की सिफारिश करेंगे। मैं व्यक्तिगत रूप से typedef पसंद करते हैं: एड enums:

typedef enum { 

    RESULT_ONE, 
    RESULT_TWO 

} RESULT; 

..because इसे टाइप/असाइनमेंट सुरक्षा प्रदान करता है।

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

struct Buffer 
{ 
    unsigned long size; 
    char* data; 
}; 

फिर अपने एपीआई दिख सकता है::

ERROR_CODE func(params... , Buffer* outBuffer); 

यह रणनीति भी अधिक विस्तृत के लिए खुल जाता है

शब्दाडंबर विकल्प 1 का इस तरह सरल यौगिकों बनाकर सीमित किया जा सकता तंत्र। उदाहरण के लिए मान लीजिए कि आप (यदि आप बफर आकार बदलना होगा जैसे) उपयोगकर्ता के लिए स्मृति को आबंटित करने में सक्षम होना चाहिए, तो आप इस के लिए एक परोक्ष दृष्टिकोण प्रदान कर सकते हैं:

struct Buffer 
{ 
    unsigned long size; 
    char* data; 
    void* (*allocator_callback)(unsigned long size); 
    void (*free_callback)(void* p); 
}; 

बेशक, इस तरह के निर्माणों की शैली हमेशा होता है गंभीर बहस के लिए खुला

शुभकामनाएं!

+0

क्या आप उस बफर संरचना को इसके बदले स्टैक पर प्रतिलिपि नहीं दे सके। इसके लिए गतिशील स्मृति का उपयोग करने के लिए यह थोड़ा बेवकूफ है (मुझे लगता है कि आप करते हैं) – toto

+1

@toto: ध्यान दें कि बफर गतिशील स्मृति होने के लिए _have_ नहीं है क्योंकि फ़ंक्शन एक पॉइंटर लेता है। यह पता द्वारा पारित एक स्टैक उदाहरण बहुत अच्छी तरह से हो सकता है। हालांकि, मैं आमतौर पर एक बफर स्ट्रक्चर का उपयोग सामान्य प्रकार के रूप में करने की वकालत करता हूं, हर बार जब आप इसे किसी फ़ंक्शन में पास करने की आवश्यकता होती है तो एक स्टैक-स्ट्रक्चर में आकार और डेटा लपेटने के बजाय। मुझे लगता है कि आप स्टैक-घोषित structs के बारे में बोलने के बारे में सोच रहे थे। – sharkin

+0

हाँ क्षमा करें, यह मेरे पास कुछ बचे हुए जावा क्षति है। ढेर पर किसी ऑब्जेक्ट के लिए एक पॉइंटर ठीक है। – toto

0

मैं पहली बार जिस तरह से करने के लिए इसी तरह यह करना चाहते हैं, लेकिन सिर्फ आसानी से विभिन्न, snprintf के मॉडल और समान कार्य के बाद:

int func(char* buffer, size_t buffer_size, input params...); 

इस तरह, अगर आप इन के बहुत सारे है, वे इसी तरह देख सकते हैं , और जहां भी उपयोगी हो, आप तर्कों की चर संख्याओं का उपयोग कर सकते हैं।

मैं नहीं बल्कि संस्करण 2 से संस्करण 1 उपयोग करने के लिए पहले से ही कहा गया है कारणों के साथ बहुत सहमत हूँ - स्मृति समस्याओं संस्करण 2

+0

मुझे नहीं लगता कि वह '...' का उपयोग करके इलिप्सिस को प्रतिबिंबित कर रहा था। यदि कुछ और नहीं है, तो एलीप्सिस हमेशा तर्क सूची में आना चाहिए। – sharkin

+0

मुझे यकीन है कि वह इसका जिक्र नहीं कर रहा था, लेकिन यह पैरामीटर को पुन: व्यवस्थित करने का विचार दिया गया। =) –

1

दोनों तरीकों का उपयोग कर के बारे में क्या साथ बहुत अधिक संभावना है? मैं शैली 1 पक्ष बनाम शैली 2. का नुकसान उत्तरों की आम सहमति से सहमत मैं अगर अपने सभी एपीआई एक लगातार नामकरण मुहावरा का पालन करें, तो जैसे शैली 2 इस्तेमाल किया जा सकता महसूस करते हैं:


// Style 1 functions 
int fooBuff(char* buffer, unsigned int buffer_size, input params...); 

// Style 2 functions 
char* fooBuffAlloc(input params...); 
bool fooBuffFree(char* foo); 

/डी

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