c++ संकलन समय पर वर्ग पदानुक्रम के कुशल विन्यास




inheritance architecture (5)

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

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

मुझे संभावना है कि वर्गों में समझाए जाने वाले मॉड्यूल की श्रेणीबद्ध ढांचे के साथ समाप्त हो जाएंगे जो तब किसी तरह दिखते हैं, 4 परतों को मानते हुए:

Product A                       Product B

Toplevel_A                      Toplevel_B                  (different for A and B, but with common parts)
    Middle_generic                  Middle_generic          (same for A and B)
        Sub_generic                     Sub_generic         (same for A and B)
            Hardware_A                      Hardware_B      (different for A and B)

यहां, कुछ वर्ग एक सामान्य बेस क्लास (जैसे Middle_generic ) से प्राप्त होते हैं, जबकि अन्य को सभी पर विशेष रूप से विशिष्ट होने की ज़रूरत नहीं होती है (जैसे Middle_generic )।

वर्तमान में मैं निम्नलिखित तरीकों के बारे में सोच सकता हूं:

  • (ए) : यदि यह एक नियमित डेस्कटॉप-अनुप्रयोग था, तो मैं आभासी विरासत का उपयोग और उदाहरणों को रन-टाइम पर बनाऊंगा, जैसे कि एक सार फैक्ट्री।

    ड्राैक : हालांकि *_B वर्गों का उत्पाद ए में कभी भी उपयोग नहीं किया जाएगा और इसलिए सभी वर्चुअल फ़ंक्शन कॉल्स की डिफरेंसिंग और रन-टाइम में किसी पते से जुड़े सदस्यों को कुछ ओवरहेड तक पहुंचने की संभावना नहीं होगी।

  • (बी) विरासत तंत्र के रूप में टेम्पलेट विशेषज्ञता का उपयोग करना (जैसे सीआरटीपी )

    template<class Derived>
    class Toplevel  { /* generic stuff ... */ };
    
    class Toplevel_A : public Toplevel<Toplevel_A> { /* specific stuff ... */ };

    दोष : समझने में मुश्किल।

  • (सी) : मेल खाने वाली फाइलों के विभिन्न सेटों का उपयोग करें और बिल्ड-स्क्रिप्ट में सही एक शामिल करें

    // common/toplevel_base.h
    class Toplevel_base { /* ... */ };
    
    // product_A/toplevel.h
    class Toplevel : Toplevel_base { /* ... */ };
    
    // product_B/toplevel.h
    class Toplevel : Toplevel_base { /* ... */ };
    
    // build_script.A
    compiler -Icommon -Iproduct_A

    दोष : भ्रामक, मुश्किल बनाए रखने और परीक्षण करने के लिए

  • (डी) : एक बड़ा टाइपफीफ़ (या # परिभाषित) फ़ाइल

    //typedef_A.h
    typedef Toplevel_A Toplevel_to_be_used;
    typedef Hardware_A Hardware_to_be_used;
    // etc.
    
    // sub_generic.h
    class sub_generic {
        Hardware_to_be_used the_hardware;
        // etc.
    };

    दोष : एक फ़ाइल को हर जगह शामिल किया जाना है और फिर भी अन्य कॉन्फ़िगरेशन के बीच वास्तव में स्विच करने के लिए एक अन्य तकनीक की आवश्यकता है।

  • (ई) : एक समान, "नीति आधारित" विन्यास, उदा

    template <class Policy>
    class Toplevel { 
        Middle_generic<Policy> the_middle;
        // ...
    };
    
    // ...
    
    template <class Policy>
    class Sub_generic {
        class Policy::Hardware_to_be_used the_hardware;
        // ... 
    };
    
    // used as
    class Policy_A {
        typedef Hardware_A Hardware_to_be_used;
    };
    Toplevel<Policy_A> the_toplevel;

    दोष : अब सब कुछ एक टेम्पलेट है; बहुत सारे कोड हर बार फिर से संकलित होने की आवश्यकता होती है

  • (एफ) : कंपाइलर स्विच और प्रीप्रोसेसर

    // sub_generic.h
    class Sub_generic {
        #if PRODUCT_IS_A
            Hardware_A _hardware;
        #endif
        #if PRODUCT_IS_B
            Hardware_B _hardware;
        #endif
    };

    ड्राबैक : ब्रर्रर ..., केवल अगर सब कुछ विफल रहता है

क्या कोई अन्य (अन्य) स्थापित डिजाइन-पैटर्न या इस समस्या का बेहतर समाधान है, जैसे कि कंपाइलर यथासंभव कई ऑब्जेक्ट्स को कोड के रूप में संभव और इनलाइन बड़े भागों में आवंटित कर सकता है, यह जानकर कि कौन सा उत्पाद बनाया जा रहा है और कौन से कक्षाएं चल रही हैं इस्तेमाल किया गया?


चूंकि यह एक कठिन वास्तविक समय एम्बेडेड सिस्टम के लिए है, इसलिए आप आमतौर पर सी का समाधान नहीं करेंगे c ++

आधुनिक कंपाइलर्स के साथ मैं कह सकता हूं कि सी ++ का ओवरहेड इतना महान नहीं है, इसलिए यह पूरी तरह से प्रदर्शन की बात नहीं है, लेकिन एम्बेडेड सिस्टम सी ++ की बजाय सी पसंद करते हैं। क्या आप एक क्लासिक डिवाइस ड्राइवर लाइब्रेरी (जैसे ftdi चिप्स के लिए एक) की तरह दिखाना चाहते हैं।

दृष्टिकोण वहाँ होगा (क्योंकि यह सी में लिखा गया है) आपके एफ के समान है, लेकिन कोई संकलन समय विकल्प नहीं है - आप कोड को खास तौर पर पीआईडी, वीआईडी, एसएन, आदि जैसे कोड के अनुसार क्रमशः पर विशेष करेंगे।

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

उम्मीद है की यह मदद करेगा...


पहले मुझे बताएं कि आप मूलतः प्रश्न में अपने प्रश्न का उत्तर दिए हैं :-)

अगला मैं कहना चाहूंगा कि सी ++ में

सटीक प्रोग्राम प्रवाह को संकलन समय पर दिया जाता है, प्रदर्शन महत्वपूर्ण होता है और बहुत से कोड को इनलाइन कर सकते हैं

टेम्पलेट्स कहा जाता है अन्य तरीकों से सिस्टम सुविधाओं का निर्माण करने का विरोध करने वाली भाषा सुविधाओं का लाभ उठाने से डेवलपर्स के लाभ के लिए आपकी परियोजना में कोड को संरचित करने का एक तार्किक तरीका होगा।

इसके अलावा, जैसा कि अन्य उत्तरों में बताया गया है कि सी ++ की तुलना में कठिन वास्तविक समय प्रणालियों के लिए और अधिक आम है, और सी में मैक्रोस पर इस तरह का अनुकूलन करने के लिए समय संकलन करने के लिए प्रथा है।

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

तो आप का मेरा उत्तर है, सी के लिए सी या खाई सी ++ के लिए


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

मैं फिर विकल्प बी में ले जाया जाता हूं अगर ए काम नहीं करता है। यह वाकई पूरी तरह से स्पष्ट नहीं है, लेकिन यह है कि लगभग, कैसे एलएलवीएम / क्लैग हार्डवेयर और ओएस के संयोजन के लिए इस समस्या को हल करता है: https://github.com/llvm-mirror/clang/blob/master/lib/Basic /Targets.cpp


एफ के लिए सोचा एक ऐसी ही ट्रेन पर, आपके पास इस तरह एक निर्देशिका लेआउट हो सकता है:

Hardware/
  common/inc/hardware.h
  hardware1/src/hardware.cpp
  hardware2/src/hardware.cpp

इंटरफ़ेस को सरल बनाने के लिए केवल एक हार्डवेयर मानें:

// sub_generic.h
class Sub_generic {
        Hardware _hardware;
};

और उसके बाद ही उस फ़ोल्डर के लिए हार्डवेयर के लिए। सीपीपी फाइलें वाले फ़ोल्डर को संकलित करें।

इस दृष्टिकोण के लाभ हैं:

  • यह समझना सरल है कि क्या हो रहा है और हार्डवेयर 3 जोड़ने के लिए
  • hardware.h अभी भी आपके एपीआई के रूप में कार्य करता है
  • यह संकलक से अमूर्त को दूर ले जाता है (आपकी गति संबंधी चिंताओं के लिए)
  • कम्पाइलर 1 को हार्डवेयर 2 सीपीपी या हार्डवेयर 3 सीपीपी को संकलित करने की आवश्यकता नहीं है जिसमें चीजें शामिल हो सकती हैं कंपाइलर 1 नहीं कर सकता (जैसे इनलाइन असेंबली, या कुछ अन्य विशिष्ट संकलक 2 चीज)
  • हार्डवेयर 3 किसी कारण के लिए बहुत जटिल हो सकता है, जिसे आपने अभी तक नहीं माना है .. इसलिए उसे पूरी निर्देशिका संरचना प्रदान करती है।

मैं समझता हूं कि आपके पास दो महत्वपूर्ण आवश्यकताएं हैं:

  1. डेटा प्रकार संकलन समय पर जाना जाता है
  2. कार्यक्रम के प्रवाह को संकलन समय पर जाना जाता है

सीआरटीपी वास्तव में उस समस्या को हल नहीं करेगा जो आप हल करने की कोशिश कर रहे हैं, क्योंकि यह HardwareLayer Sub_generic को सब- Sub_generic , Middle_generic या TopLevel Middle_generic पर तरीकों को कॉल करने की Sub_generic TopLevel और मुझे विश्वास नहीं होता कि आप यही चाहते हैं।

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

class Hardware_A {};
class Hardware_B {};

तो फिर एक क्लास पर विचार करें जो कि Hardware_A संबंधित सामान्य केस का वर्णन करता है।

template <typename Hardware>
class HardwareLayer
{
public:
    typedef long int64_t;

    static int64_t getCPUSerialNumber() {return 0;}
};

अब हम हार्डवेयर के लिए विशेषज्ञता देखते हैं:

template <>
class HardwareLayer<Hardware_B>
{
public:
    typedef int int64_t;

    static int64_t getCPUSerialNumber() {return 1;}
};

अब, यहां सब-गनरिक परत में उपयोग उदाहरण है:

template <typename Hardware>
class Sub_generic
{
public:
    typedef HardwareLayer<Hardware> HwLayer;
    typedef typename HwLayer::int64_t int64_t;

    int64_t doSomething() {return HwLayer::getCPUSerialNumber();}
};

और अंत में, एक छोटा मुख्य जो दोनों कोड पथ को निष्पादित करता है और दोनों डेटा प्रकारों का उपयोग करता है:

int main(int argc, const char * argv[]) {
    std::cout << "Hardware_A : " << Sub_generic<Hardware_A>().doSomething() << std::endl;
    std::cout << "Hardware_B : " << Sub_generic<Hardware_B>().doSomething() << std::endl;
}

अब अगर आपके हार्डवेयर लेयर को राज्य बनाए रखने की आवश्यकता है, तो यहां एक और तरीका है जो हार्ड लेयर और सब-गनरिक लेयर क्लायर्स को लागू करने का है।

template <typename Hardware>
class HardwareLayer
{
public:
    typedef long hwint64_t;

    hwint64_t getCPUSerialNumber() {return mySerial;}

private:
    hwint64_t mySerial = 0;
};

template <>
class HardwareLayer<Hardware_B>
{
public:
    typedef int hwint64_t;

    hwint64_t getCPUSerialNumber() {return mySerial;}

private:
    hwint64_t mySerial = 1;
};

template <typename Hardware>
class Sub_generic : public HardwareLayer<Hardware>
{
public:
    typedef HardwareLayer<Hardware> HwLayer;
    typedef typename HwLayer::hwint64_t hwint64_t;

    hwint64_t doSomething() {return HwLayer::getCPUSerialNumber();}
};

और यहां एक अंतिम रूप है जहां केवल उप-गनरिकारी कार्यान्वयन में परिवर्तन होता है:

template <typename Hardware>
class Sub_generic
{
public:
    typedef HardwareLayer<Hardware> HwLayer;
    typedef typename HwLayer::hwint64_t hwint64_t;

    hwint64_t doSomething() {return hw.getCPUSerialNumber();}

private:
    HwLayer hw;
};






real-time