2010-09-16 9 views
7

वस्तुओं के स्वामित्व वाले लाइब्रेरी नियंत्रण सत्रों में सी एपीआई को देखते हुए, सीएआई एपीआई को आरएआईआई सी ++ कक्षाओं में समाहित करने के लिए सबसे अच्छा डिज़ाइन क्या है?RAII C++ कक्षाओं में सी एपीआई को कैसे समाहित किया जाए?

सी एपीआई लगता है:

HANDLE OpenSession(STRING sessionID); 
void CloseSession(HANDLE hSession); 
HANDLE OpenItem(HANDLE hSession, STRING itemID); 
void CloseItem(HANDLE hItem); 

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

मेरी कक्षाओं के डिजाइन के लिए मेरा पहला विचार शुद्ध और प्रत्यक्ष आरएआईआई है। निहित वर्ग कंटेनर ऑब्जेक्ट को कन्स्ट्रक्टर पैरामीटर के रूप में स्वीकार करता है।

class Session { 
    HANDLE const m_hSession; 
public: 
    Session(STRING sessionID): m_hSession(OpenSession(sessionID)) {} 
    ~Session() { CloseSession(m_hSession); } 
}; 
class Item { 
    HANDLE const m_hItem; 
public: 
    Item(HANDLE hSession, STRING itemID): m_hItem(OpenItem(hSession, itemID)) {} 
    ~Item() { CloseItem(m_hItem); } 
}; 

इस डिजाइन एक बुरा व्यवहार की अनुमति के नुकसान है: एक सत्र वस्तु विलुप्त जा सकता है (और CloseSession समारोह कहा जाता है) से पहले अपने आइटम के सभी ऑब्जेक्ट के विलुप्त कर दिया है। यह कष्टप्रद है, क्योंकि ऐसा नहीं होना चाहिए। भले ही यह गलत व्यवहार संभव है, इसलिए सी एपीआई का उपयोग करके मान्य नहीं है, मैं इसे सी ++ एपीआई में डिज़ाइन से टालना चाहता हूं।

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

class Item { 
    HANDLE const m_hItem; 
    Item(HANDLE hSession, STRING itemID): m_hItem(OpenItem(hSession, itemID) {} 
    ~Item() { CloseItem(m_hItem); } 
    friend class Session; 
public: 
}; 
class Session { 
    HANDLE const m_hSession; 
    typedef vector<Item *> VecItem; 
    VecItem m_vecItem; 
    Session(STRING sessionID): m_hSession(OpenSession(sessionID)) {} 
    ~Session() { 
     for (size_t n = 0 ; n < m_vecItem.size() ; ++n) delete m_vecItem[n]; 
     m_vecItem.clear(); 
     CloseSession(m_hSession); 
     } 
public: 
    Item * OpenItem(STRING itemID) { 
     Item *p = new Item(m_hSession, itemID); 
     m_vecItem.push_back(p); 
     return p; 
     } 
    void CloseItem(Item * item) { 
     VecItem::iterator it = find(m_vecItem.begin(), m_vecItem.end(), item); 
     if (it != m_vecItem.end()) { 
      Item *p = *it; m_vecItem.erase(it); delete p; 
      } 
     } 
}; 

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

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

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

कोई बेहतर डिजाइन विचार?

+0

यदि हम मानते हैं कि मानक आरएआईआई रैपिंग ठीक है, तो 2 प्रश्न हैं: सत्र में आइटम खोलने/बंद करने का सबसे सुरुचिपूर्ण (आरएआईआई) तरीका क्या है और पॉइंटर्स के वेक्टर के लिए एक विकल्प क्या है, है ना? – stefaanv

+0

आदर्श रूप से, मैं वस्तुओं को खोलने/बंद करने के लिए सुरुचिपूर्ण आरएआईआई तरीका, और सत्र से संबंधित वस्तुओं को देखना चाहता हूं ताकि वे सत्र से पहले बंद हो जाएं। (पॉइंटर्स इश्यू का वेक्टर सवाल नहीं है) –

+0

मेरे लिए, आपके द्वारा दिखाए गए 2 उदाहरण एक अलग उद्देश्य प्रदान करते हैं चाहे आप अस्थायी आइटम (केवल एक समारोह कहने के दायरे में) के साथ काम करना चाहते हैं या यदि आप आइटम चाहते हैं लंबे समय तक रहना आरएआईआई जब तक सत्र जीवित रहता है तब तक वस्तुओं को साफ़ करने में आपकी सहायता नहीं कर सकता है। – stefaanv

उत्तर

5

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

class SessionHandle 
{ 
    explicit SessionHandle(HANDLE in_h) : h(in_h) {} 
    HANDLE h; 
    ~SessionHandle() { if(h) CloseSession(h); } 
}; 

class ItemHandle 
{ 
    explicit ItemHandle(HANDLE in_h) : h(in_h) {} 
    HANDLE h; 
    ~ItemHandle() { if(h) CloseItem(h); } 
}; 

class Session 
{ 
    explicit Session(STRING sessionID) : session_handle(OpenSession(sessionID)) 
    { 
    } 
    shared_ptr<SessionHandle> session_handle; 
}; 

class Item 
{ 
    Item(Session & s, STRING itemID) : 
    item_handle(OpenItem(s.session_handle.get(), itemID)), 
    session_handle(s.session_handle) 
    { 
    } 
    shared_ptr<ItemHandle> item_handle; 
    shared_ptr<SessionHandle> session_handle; 
}; 
+0

यदि ओपनआईटम और ओपनसेशन न्यूल वापस कर सकता है, तो आप या तो उस मामले में फेंकने के लिए रचनाकारों को बदलना चाहते हैं, या नल-ऑब्जेक्ट पैटर्न का उपयोग कर सकते हैं। –

+1

मुझे यह पसंद नहीं है कि 'सत्र' रिसाव हो सकता है यदि उसके 'आइटम' में से कोई भी जीवित रहता है। जब भी संभव हो मैं निर्धारक जीवनकाल पसंद करता हूं, आसान डीबगिंग के लिए बनाता है। –

+0

@ मैथ्यूयू एम। आपके उदाहरण और खान दोनों में आपको अपनी वस्तुओं के जीवनकाल के बारे में सावधान रहना होगा। यदि आप सावधान नहीं हैं तो आपको रिसाव मिलती है, आपके भीतर आपको एक त्रुटि मिलती है। यह एक डिजाइन निर्णय है जिसे उपयोगकर्ता को दोनों मामलों में अवगत होना चाहिए। (यह कहकर कि मुझे आपकी डिज़ाइन बहुत पसंद है, +1) –

2

यह एक दिलचस्प समस्या मुझे लगता है कि है।

सबसे पहले, आरएआईआई के लिए, आप आम तौर पर कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर को सामान्य रूप से कार्यान्वित करना चाहते हैं, यहां HANDLE const उन्हें रोक देगा, लेकिन क्या आप वास्तव में उन वस्तुओं को चाहते हैं जिन्हें कॉपी नहीं किया जा सकता है? और बेहतर उन्हें अपवाद भी सुरक्षित बनाते हैं।

इसके अलावा, id का मुद्दा भी है: क्या आपको विशिष्टता सुनिश्चित करना है या फ्रेमवर्क आपके लिए यह करता है?

संपादित:

आवश्यकताओं मेरा पहला जवाब के बाद से precised किया गया है, अर्थात्:

  • पुस्तकालय संदर्भ गिनती पहले से ही लागू करता है, कोई जरूरत नहीं इसे अपने आप को संभालने के लिए

में इस मामले में, आपके पास दो डिज़ाइन विकल्प हैं:

    012 , Item Session इसके साथ बनाया गया था वापस करने के लिए जुड़ा हुआ है Session एक सत्र प्रबंधक इस सत्र के मालिक सत्र प्रबंधक होने और Item क्वेरी प्रबंधक होने से स्वचालित है का उपयोग कर इसे जब यह मर जाता है सूचित करता है (:
  • Observer पैटर्न का उपयोग करें इसके सत्र)
  • उपयोग @ माइकल की योजना के बारे में जो Item रों शेयर में Session वस्तु का स्वामित्व है, ताकि Session जबकि कम से कम एक Item अभी भी मौजूद हैं नष्ट नहीं किया जा सकता है।

मुझे दूसरा समाधान पसंद नहीं है क्योंकि Session का जीवनकाल तब ट्रैक करना बहुत कठिन है: आप इसे विश्वसनीय रूप से मार नहीं सकते हैं।

दूसरी ओर, के रूप में आप ने कहा, पहले समाधान अशक्त वस्तुओं, जो स्वीकार्य नहीं हो सकता है के अस्तित्व का तात्पर्य।

पुरानी समाधान:

वास्तविक डिजाइन का सवाल है, मेरा प्रस्ताव होगा:

class Item 
{ 
public: 
    Item(): mHandle() {} 

    Item(Session& session, std::string id): mHandle(session.CreateItem(id)) 
    { 
    } 

    void swap(Item& rhs) 
    { 
    using std::swap; 
    swap(mHandle, rhs.mHandle); 
    } 

    void reset() 
    { 
    mHandle.reset(); 
    } 

    /// Defensive Programming 
    void do() 
    { 
    assert(mHandle.exists() && "do - no item"); 
    // do 
    } 

private: 
    boost::weak_ptr<HANDLE const> mHandle; 
}; 

और सत्र वर्ग

class Session 
{ 
public: 

private: 
    typedef boost::weak_ptr<HANDLE const> weak_ptr; 
    typedef boost::shared_ptr<HANDLE const> shared_ptr; 
    typedef boost::unordered_map<std::string, shared_ptr> map_type; 

    friend class Item; 
    struct ItemDeleter 
    { 
    void operator()(HANDLE const* p) { CloseItem(*p); } 
    }; 

    weak_ptr CreateItem(std::string const& id) 
    { 
    map_type::iterator it = mItems.find(id); 
    if (it != mItems.end()) return it->second; 

    shared_ptr p = shared_ptr(new OpenItem(mHandle, id), ItemDeleter()); 
    std::pair<map_type::iterator, bool> result = 
     mItems(std::make_pair(id, p)); 

    return result.first->second; 
    } 

    map_type mItems; 
    HANDLE const mHandle; 
}; 

यह जिसका अर्थ है आप के लिए कहा बता देते हैं:

    जब भी Session मरता, सभी HANDLE:
  • Session वस्तु Item रों के जीवनकाल के प्रबंधन के लिए जिम्मेदार है, वास्तविक Item वस्तु संभाल
  • के लिए एक प्रॉक्सी से अधिक नहीं किया जा रहा है आप अपने वस्तुओं की एक नियतात्मक जीवन है आइटम के लिए प्रभावी रूप से बंद हो जाती हैं

सूक्ष्म मुद्दों: इस कोड को एक बहु सूत्रण आवेदन में सुरक्षित नहीं है, लेकिन फिर मैं पता नहीं है कि क्या हम पूरी तरह से क्रमानुसार करने OpenItem और CloseItem को पहुँचता जरूरत के रूप में मैं अगर अंतर्निहित पता नहीं है लाइब्रेरी थ्रेड-सुरक्षित है।

ध्यान दें कि इस डिज़ाइन में Session ऑब्जेक्ट की प्रतिलिपि नहीं बनाई जा सकती है। जाहिर है हम एक SessionManager वस्तु बना सकते हैं (आम तौर पर एक सिंगलटन, लेकिन यह आवश्यक नहीं है) और उसे बहुत उसी तरह :)

+0

आरएआईआई के लिए, कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर के लिए ** कोई पूर्ण ** आवश्यकता ** नहीं है। मूव कन्स्ट्रक्टर और मूव ऑपरेटर के रूप में चाल semantics प्रदान करना पर्याप्त है। वैसे भी, यह न्यूल क्लास आइटम अस्तित्व से नहीं बचता है। –

+0

@Didier: नहीं, यह नहीं है, लेकिन मुझे आपके प्रश्न में यह आवश्यकता नहीं दिखाई दे रही है। जब भी सत्र मर जाता है तो यह 'आइटम' को सही ढंग से" शून्य "करता है। यदि आप 'आइटम' को वैध रखना चाहते हैं, तो कृपया @ माइकल के उत्तर पर विचार करें। आपको 'सत्र' के जीवनकाल को नियंत्रित करने में कठिनाई होगी, लेकिन आप अपने 'आइटम की अब तक अमान्य नहीं होंगे। –

1

पर STLSoft की टिप्पणी का विस्तार करने के लिए, STLSoft के scoped_handle स्मार्ट सूचक का उपयोग में Session रों का प्रबंधन , के रूप में में:

if(hSession != -1) { 
// Handle failure to open session 
} 
else { 
    stlsoft::scoped_handle<HANDLE> session_release(hSession, CloseSession, -1); 

HTH

+०१२३५१६४१०६:

HANDLE hSession = OpenSession("session-X"); 
if(!hSession) { 
// Handle failure to open session 
} 
else { 
    stlsoft::scoped_handle<HANDLE> session_release(hSession, CloseSession); 

    HANDLE hItem = OpenItem(hSession, "item-Y"); 
    if(!hItem) { 
    // Handle failure to open item 
    } 
    else { 
     stlsoft::scoped_handle<HANDLE> item_release(hItem, CloseItem); 

    // Use item 
    } 
} 

हैं "अशक्त" संभाल मान 0 नहीं है, तो कुछ इस तरह करते हैं

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