विकास के दौरान विभिन्न प्रकार के व्यवहार के बीच स्विच करने के लिए #ifdef का उपयोग करना


28

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

मैं आमतौर पर कई पहचानकर्ताओं का परिचय देता हूं और कुछ ऐसा करता हूं

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

void bar()
{
    doSomething3();
#ifndef APPROACH3
    doSomething4();
#endif
    doSomething5();
#ifdef APPROACH2
    bar_approach2();
#endif
}

int main()
{
    foo();
    bar();
    return 0;
}

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



2
चूंकि आप विकास के बारे में बात कर रहे हैं, मेरा मानना ​​है कि आपको अलग-अलग कार्यान्वयन के साथ स्विच करने और प्रयोग करने में जो कुछ भी आसान लगता है वह आपको करना चाहिए। यह विकास के दौरान व्यक्तिगत प्राथमिकताओं की तरह है, किसी विशिष्ट समस्या को हल करने के लिए कुछ सर्वोत्तम अभ्यास नहीं।
इमर्सन कार्डसो

1
मैं रणनीति पैटर्न या अच्छे ol 'बहुरूपता का उपयोग करने की सलाह दूंगा, क्योंकि यह स्विचेबल व्यवहार के लिए एकल प्लग-इन बिंदु रखने में मदद करता है।
pmf

4
ध्यान रखें कि #ifdefब्लॉक बंद होने पर कुछ IDE ब्लॉक का कुछ भी मूल्यांकन नहीं करते हैं । हम उन मामलों में भाग गए जहां कोड आसानी से बासी हो सकता है और यदि आप सभी मार्गों को नियमित रूप से नहीं बनाते हैं तो संकलन नहीं कर सकते हैं।
बेरिन लोरिट्स

इस उत्तर पर एक नज़र डालिए मैंने एक और प्रश्न दिया। यह बहुत #ifdefsकम बोझिल बनाने के कुछ तरीके देता है ।
user1118321

जवाबों:


9

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


gitविशेष रूप से इस तरह की बात पर अडिग है। शायद svn, hgया अन्य के साथ ऐसा नहीं है , लेकिन यह अभी भी किया जा सकता है।
19

यही मेरा शुरुआती विचार भी था। "कुछ अलग करने के साथ गड़बड़ करना चाहते हैं?" git branch!
वेस टोलमैन

42

जब आप एक हथौड़ा पकड़ रहे हैं, तो सब कुछ एक नाखून की तरह दिखता है। #ifdefआपके कार्यक्रम में कस्टम व्यवहार प्राप्त करने के लिए एक प्रकार के साधन के रूप में इसका उपयोग करने के लिए कैसे काम करता है यह जानने के बाद यह आपको लुभा रहा है । मुझे पता है क्योंकि मैंने वही गलती की थी।

मुझे MFC C ++ में लिखा एक विरासत कार्यक्रम विरासत में मिला है जो पहले से ही #ifdefमंच-विशिष्ट मूल्यों को परिभाषित करने के लिए उपयोग किया जाता है। इसका मतलब है कि मैं अपने कार्यक्रम को एक 32-बिट प्लेटफॉर्म या 64-बिट प्लेटफॉर्म पर केवल विशिष्ट मैक्रो मानों को परिभाषित करने (या कुछ मामलों में परिभाषित न करने वाले) में उपयोग करने के लिए संकलित कर सकता हूं।

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

मुझे प्रलोभन दिया गया, और मैंने दिया। मैंने #ifdefअपने कोड में विभिन्न व्यवहार को अलग करने के लिए अनुभाग लिखे । कोई गलती न करें, यह पहली बार में शीर्ष पर कुछ भी नहीं था। बहुत मामूली व्यवहार परिवर्तन किए गए थे जो मुझे हमारे ग्राहकों के लिए कार्यक्रम के संस्करणों को फिर से वितरित करने की अनुमति देते थे और मुझे कोड आधार के एक से अधिक संस्करण की आवश्यकता नहीं थी।

समय के साथ यह रखरखाव नरक बन गया वैसे भी , क्योंकि कार्यक्रम अब बोर्ड भर में लगातार व्यवहार किया। अगर मैं कार्यक्रम के एक संस्करण का परीक्षण करना चाहता था, तो मुझे यह जानना जरूरी था कि ग्राहक कौन था। कोड, हालांकि मैंने इसे एक या दो हेडर फ़ाइलों में कम करने की कोशिश की थी, बहुत क्लॉट किया गया था और त्वरित फिक्स दृष्टिकोण जो #ifdefप्रदान करता था, जिसका अर्थ था कि इस तरह के समाधान एक घातक कैंसर की तरह पूरे कार्यक्रम में फैल गए।

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


डिबग संस्करणों के बारे में क्या है, जैसे "यदि डिबग, डिफाइन चर x ..." ऐसा लगता है कि यह लॉगिंग जैसी चीजों के लिए उपयोगी हो सकता है, लेकिन फिर यह पूरी तरह से बदल भी सकता है कि डिबग सक्षम होने पर आपका कार्यक्रम कैसे काम करता है और कब नहीं है ।
whn

8
@snb मैंने उसके बारे में सोचा। मैं अभी भी एक कॉन्फ़िगरेशन फ़ाइल को बदलने और अधिक विवरण के साथ लॉग बनाने में सक्षम होना पसंद करता हूं। अन्यथा उत्पादन में कार्यक्रम के साथ कुछ गलत हो जाता है, और आपके पास निष्पादन योग्य को पूरी तरह से बदलने के बिना इसे डीबग करने का कोई तरीका नहीं है। आदर्श स्थितियों में भी, यह वांछनीय से कम है। ;)
नील

ओह, यह डिबग करने के लिए recompile नहीं करने के लिए और अधिक आदर्श होगा, मैंने उस बारे में नहीं सोचा था!
whn

9
आप जो वर्णन कर रहे हैं, उसके एक चरम उदाहरण के लिए, इस लेख में "इस समस्या का रख-रखाव" के तहत 2 पैराग्राफ को देखें कि क्यों एमएस इस बिंदु पर पहुंचा जहां उन्हें कुछ साल पहले खरोंच से अपने अधिकांश सी रनटाइम को फिर से लिखना पड़ा। । blogs.msdn.microsoft.com/vcblog/2014/06/10/…
Dan Neely

2
@snb अधिकांश लॉगिंग लाइब्रेरी एक लॉगिंग स्तर तंत्र मानती हैं। यदि आप डिबगिंग के दौरान लॉग की गई कुछ जानकारी चाहते हैं, तो आप इसे कम लॉगिंग स्तर ("डीबग" या "वर्बोस") के साथ लॉग करते हैं। फिर एप्लिकेशन के पास एक कॉन्फ़िगरेशन पैरामीटर है जो यह बताता है कि लॉग किस स्तर पर है। तो इस समस्या के लिए जवाब अभी भी विन्यास है। यह भी एक ग्राहक वातावरण में इस कम प्रवेश स्तर को चालू करने में सक्षम होने का भारी लाभ है
jpmc26

21

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

लेकिन चेतावनी का एक शब्द: #ifdef शाखाओं को न रखें मेरे समय को बर्बाद करने की तुलना में बहुत अधिक निराशा होती है एक ही चीज को चार अलग-अलग तरीकों से लागू किया जाता है, केवल यह जानने के लिए कि मुझे कौन सा पढ़ना चाहिए

#Ifdef पर पढ़ना प्रयास करता है क्योंकि आपको वास्तव में इसे छोड़ना याद रखना है! इसे पूरी तरह से होने की तुलना में किसी भी कठिन मत बनाओ।

जितना हो सके #ifdefs का इस्तेमाल करें। आमतौर पर ऐसे तरीके हैं जो आप अपने विकास के माहौल में स्थायी मतभेदों के लिए कर सकते हैं , जैसे कि डिबग / रिलीज़ बिल्ड, या विभिन्न आर्किटेक्चर के लिए।

मैंने ऐसी लाइब्रेरी विशेषताएँ लिखी हैं जो शामिल लाइब्रेरी संस्करणों पर निर्भर थीं, जिन्हें #ifdef विभाजन की आवश्यकता थी। इसलिए कई बार यह एकमात्र तरीका या सबसे आसान तरीका हो सकता है, लेकिन फिर भी आपको उन्हें बनाए रखने के लिए परेशान होना चाहिए।


1

#Ifdefs का उपयोग करना कोड को पढ़ने में बहुत कठिन बनाता है।

तो, नहीं, उस तरह #ifdefs का उपयोग न करें।

कुछ तर्क हो सकते हैं कि क्यों नहीं ifdefs का उपयोग किया जाए, मेरे लिए यह पर्याप्त है।

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

यह कर सकता है बहुत सारी चीजें:

void foo()
{
    doSomething1();
    doSomething2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
}

void foo()
{
    doSomething1();
    doSomething2();
    foo_approach2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
    foo_approach2();
}

सभी इस बात पर निर्भर करते हैं कि कौन से दृष्टिकोण परिभाषित हैं या नहीं। यह क्या करता है पहली नज़र में बिल्कुल स्पष्ट नहीं है।

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