2012-04-11 11 views
5

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

#include <value1.h> 
#include <value2.h> 
#include <value3.h> 

... 

if (value1()) 
{ 
    // do something 
} 

bool b = value2(); 

if (b && anotherCondition) 
{ 
    // do more stuff 
} 

if (value3() < 10) 
{ 
    // more stuff again 
} 

जहां मूल्य वापसी के लिए कॉल या तो एक bool या किसी पूर्णांक। जब से मैं मान कि इन कॉल हमेशा लौट पता है, मैं अपने सामान्य मूल्यों के लिए कॉल विस्तार करने के लिए कुछ regex प्रतिस्थापन किया है:

// where: 
// value1() == true 
// value2() == false 
// value3() == 4 

// TODO: Remove expanded config (value1) 
if (true) 
{ 
    // do something 
} 

// TODO: Remove expanded config (value2) 
bool b = false; 

if (b && anotherCondition) 
{ 
    // do more stuff 
} 

// TODO: Remove expanded config (value3) 
if (4 < 10) 
{ 
    // more stuff again 
} 

नोट हालांकि मूल्यों तय कर रहे हैं, वे संकलन समय पर सेट नहीं हैं कि लेकिन साझा स्मृति से पढ़े जाते हैं इसलिए संकलक वर्तमान में दृश्यों के पीछे कुछ भी अनुकूलित नहीं कर रहा है।

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

// do something 

// DONT do more stuff (b being false always prevented this) 

// more stuff again 

दिक्कत यह है कि मैं है अंतिम साफ कोड प्राप्त करने के लिए दूसरे, सही लेकिन मूर्ख, चरण से प्राप्त करने के लिए सैकड़ों (संभवतः हजारों) परिवर्तन हैं।

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

मुझे पता है कि ऐसे ही प्रश्न हैं लेकिन मुझे इस तरह की कोई आवश्यकता नहीं मिल रही है और यह भी सोचा कि क्या कोई उपकरण या प्रक्रियाएं तब से उभरीं जब से उनसे पूछा गया था?

उत्तर

5

ऐसा लगता है कि आप मैं व्यवहार में "ज़ोंबी कोड" क्या कहते हैं ... मृत, लेकिन अभी भी जहाँ तक संकलक का संबंध है रहते है।संगठित रनटाइम कॉन्फ़िगरेशन चर के अधिकांश सिस्टमों के साथ यह एक बहुत ही आम समस्या है: अंत में कुछ कॉन्फ़िगरेशन चर स्थायी स्थायी स्थिति पर आते हैं, फिर भी रनटाइम पर बार-बार पुनर्मूल्यांकन किया जाता है।

इलाज रेगेक्स नहीं है, जैसा कि आपने नोट किया है, क्योंकि रेगेक्स सी ++ कोड को विश्वसनीय रूप से पार्स नहीं करता है। आपको program transformation system की आवश्यकता है। यह एक उपकरण है जो वास्तव में पार्स स्रोत कोड है, और पार्स पेड़ पर कोड-टू-कोड रीराइटिंग नियमों का एक सेट लागू कर सकता है, और बदले गए पेड़ से स्रोत टेक्स्ट को पुन: उत्पन्न कर सकता है।

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

हमारे DMS Software Reengineering Toolkit इसके C++(11) front end के साथ (और इसका उपयोग किया गया है) सी ++ स्रोत कोड पर बड़े पैमाने पर परिवर्तन करने के लिए कर सकते हैं, और स्रोत-से-स्रोत परिवर्तन हैं। AFAIK, यह एकमात्र उत्पादन उपकरण है जो यह कर सकता है। आपको जो चाहिए वह परिवर्तनों का एक सेट है जो ब्याज के कॉन्फ़िगरेशन चर के अंतिम राज्य के बारे में आपके ज्ञान का प्रतिनिधित्व करता है, और कुछ सरल कोड सरलीकरण नियम। निम्नलिखित डीएमएस नियम क्या आप की संभावना चाहते हैं के करीब हैं:

rule fix_value1():expression->expression 
    "value1()" -> "true"; 
    rule fix_value2():expression->expression 
    "value2()" -> "false"; 
    rule fix_value3():expression->expression 
    "value3()" -> "4"; 

    rule simplify_boolean_and_true(r:relation):condition->condition 
    "r && true" -> "r". 
    rule simplify_boolean_or_ture(r:relation):condition->condition 
    "r || true" -> "true". 
    rule simplify_boolean_and_false(r:relation):condition->condition 
    "r && false" -> "false". 
    ... 
    rule simplify_boolean_not_true(r:relation):condition->condition 
    "!true" -> "false". 
    ... 

    rule simplify_if_then_false(s:statement): statement->statement 
     " if (false) \s" -> ";"; 
    rule simplify_if_then_true(s:statement): statement->statement 
     " if (true) \s" -> "\s"; 
    rule simplify_if_then_else_false(s1:statement, s2:statement): statement->statement 
     " if (false) \s1 else \s2" -> "\s2"; 
    rule simplify_if_then_else_true(s1:statement, s2: statement): statement->statement 
     " if (true) \s1 else \s2" -> "\s2"; 

तुम भी नियमों की जरूरत है सरल करने के लिए ("गुना") लगातार गणित से जुड़े भाव, और नियमों को भाव है कि अब स्थिर है पर स्विच संभालने के लिए। यह देखने के लिए कि पूर्णांक निरंतर फोल्डिंग के लिए डीएमएस नियम कैसा दिखते हैं Algebra as a DMS domain देखें।

रेगेक्स के विपरीत, डीएमएस पुन: लिखने के नियम कोड "मेल नहीं खाते"; वे संबंधित एएसटी का प्रतिनिधित्व करते हैं और यह है कि एएसटी मिलान कर रहे हैं। चूंकि यह एएसटी मिलान है, इसलिए उन्हें व्हाइटस्पेस, लाइन ब्रेक या टिप्पणियों के साथ कोई समस्या नहीं है। आपको लगता है कि उन्हें ऑपरेटरों के आदेश में परेशानी हो सकती है ('क्या होगा अगर' झूठी & & x "सामना किया गया है? '); वे नहीं हैं, व्याकरण नियम & & और || को डीएमएस सी ++ पार्सर में सहयोगी और कम्यूटेटिव के रूप में चिह्नित किया गया है और मिलान प्रक्रिया स्वचालित रूप से इसे ध्यान में रखती है।

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

(संपादित करें फरवरी 2015: अब पूर्ण सी ++ 14; कार्यों/विधियों के भीतर प्रवाह विश्लेषण)।

हमने वास्तव में उत्कृष्ट सफलता के साथ लगभग एक दशक पहले आईबीएम टिवोली से मिश्रित सी और सी ++ कोड के 1.5 एम एसएलओसी आवेदन के लिए इस तकनीक को लागू किया; हमें प्रवाह विश्लेषण की आवश्यकता नहीं थी: -}

+0

हां, यह वही है जो मैं चाहता हूं। Regexes मुझे थोड़ा सा मिल सकता है, लेकिन वे अपरिचित हैं और जैसा कि आप कहते हैं, महत्वपूर्ण रूप से, वे भाषा 'नहीं' जानते हैं। बहुत बहुत धन्यवाद। Btw (अपने "डीएमएस सॉफ्टवेयर पुनर्रचना टूलकिट" लिंक टूट गया है) –

+0

मूलरूप regexes कुछ भी घोंसले को शामिल के साथ सौदा नहीं कर सकते। (बूलियन) अभिव्यक्तियों में घोंसला शामिल है। QED: regexes स्वयं अभिव्यक्तियों पर काम नहीं कर सकते हैं। पीएस: लिंक तय है। –

1

आप कहते हैं:

ध्यान दें कि हालांकि मूल्यों यथोचित तय कर रहे हैं, वे संकलन समय पर सेट नहीं हैं लेकिन साझा स्मृति से पढ़ा जाता है तो संकलक वर्तमान में पर्दे के पीछे दूर कुछ भी अनुकूलन के नहीं है।

निरंतर तह हाथ से मूल्यों भावना का एक बहुत जब तक वे पूरी तरह से तय कर रहे हैं नहीं है। अपने संकलक प्रदान करता है तो constexpr आप इस्तेमाल कर सकते हैं कि, या आप इस तरह पूर्वप्रक्रमक मैक्रो में स्थानापन्न कर सकते हैं:

#define value1() true 
#define value2() false 
#define value3() 4 

अनुकूलक वहाँ से आप की देखभाल करेंगे। आपके <valueX.h> हेडर में क्या है, यह जानने के बिना या साझा स्मृति से इन मानों को प्राप्त करने की आपकी प्रक्रिया कैसे काम कर रही है, यह जानने के बिना, मैं केवल यह बता दूंगा कि मौजूदा मानएक्स() फ़ंक्शंस का नाम बदलने और रनटाइम चेक करने में उपयोगी हो सकता है वे भविष्य में फिर से बदलने के मामले:

// call this at startup to make sure our agreed on values haven't changed 
void check_values() { 
    assert(value1() == get_value1_from_shared_memory()); 
    assert(value2() == get_value2_from_shared_memory()); 
    assert(value3() == get_value3_from_shared_memory()); 
} 
+0

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

+0

यदि आपको अपने सिर में अभिव्यक्तियों का मूल्यांकन करने में कठिनाई हो रही है, तो पहुंचने योग्य कोड खोजने के लिए एक गरीब व्यक्ति का परीक्षण मेरे सुझाव का पालन करना होगा और फिर जीसीसी की '-उनेरटेबल-कोड' चेतावनी (जो आमतौर पर बंद होता है) '-वॉल' के साथ)। मेरा अनुमान है कि सी ++ के लिए कोई भी मुफ्त कोड-रीराइटिंग टूल जो आपके लिए यह करेगा, वह संभवतः इसे करने से पहले स्थापित करने में अधिक परेशानी होगी। यदि आप चिंतित हैं तो आप गलतियां कर सकते हैं, फिर इसे एक ही स्थान पर प्रतिस्थापित न करें ... अभिव्यक्तियों को उठाएं और अपनी समानता को अपनी सरलीकरण पर जोर दें। – HostileFork

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