2015-01-01 5 views
6

एक परियोजना में मैं हाल ही में मदद कर रहा हूं, संपूर्ण कोड बेस एक राक्षसी enum पर निर्भर करता है जो प्रभावी रूप से एक गौरवशाली हैश टेबल के लिए चाबियों के रूप में उपयोग किया जाता है। एकमात्र समस्या यह है कि यह बहुत बड़ा है, जब भी enum परिवर्तन मूल रूप से पहले से ही बड़े कोड बेस के लिए पुनर्निर्माण होता है। यह हमेशा के लिए लेता है और मैं वास्तव में इसे बदलने के लिए प्यार करता हूँ।Enums का दुरुपयोग करने के लिए एक वैकल्पिक खोजना

enum Values 
{ 
    Value = 1, 
    AnotherValue = 2, 
    <Couple Thousand Entries> 
    NumValues // Sentinel value for creating arrays of the right size 
} 

क्या मैं के लिए देख रहा हूँ तरीके इस enum को बदलने के लिए है, लेकिन अभी भी एक प्रणाली typesafe (कोई अनियंत्रित तार) है और यह भी MSVC2010 (कोई constexpr) के साथ संगत है कि है। अतिरिक्त संकलन ओवरहेड स्वीकार्य है क्योंकि यह फाइलों के समूह को दोबारा बनाने की तुलना में संकलित करने के लिए अभी भी कम समय हो सकता है।

मेरे वर्तमान प्रयास मूल रूप से लिंक समय तक मूल्यों को परिभाषित करने में देरी के रूप में संक्षेप में समझा जा सकता है।

इसके उपयोग के उदाहरण

GetValueFromDatabase(Value); 
AddValueToDatabase(Value, 5); 
int TempArray[NumValues]; 

संपादित करें: Compiletime और रनटाइम preprocessing स्वीकार्य है। रनटाइम पर किसी प्रकार की कैशिंग डेटा संरचना को बंद करने के साथ-साथ।

// key.h 

namespace keys { 

// Identifies a unique key in the database 
class Key { 
    public: 
    // The numeric ID of the key 
    virtual size_t id() const = 0; 
    // The string name of the key, useful for debugging 
    virtual const std::string& name() const = 0; 
}; 

// The total number of registered keys 
size_t count(); 

// Internal helpers. Do not use directly outside this code. 
namespace internal { 
    // Lazily allocates a new instance of a key or retrieves an existing one. 
    const Key& GetOrCreate(const std::string& name, size_t id); 
} 
} 

#define DECLARE_KEY(name) \ 
    extern const ::keys::Key& name 

#define DEFINE_KEY(name, id) \ 
    const ::keys::Key& name = ::keys::internal::GetOrCreate(STRINGIFY(name), id) 

कोड के साथ:

+0

* "जब भी enum परिवर्तन मूल रूप से पहले से ही बड़े कोड बेस के लिए पुनर्निर्माण होता है तो संकलित करना" * मुझे लगता है कि आपको स्वत: निर्माण प्रक्रिया के लिए समर्थन की आवश्यकता है?उदाहरण के लिए, यदि आप संख्याओं का नाम देने के लिए (सी ++) * पहचानकर्ता * का उपयोग करते हैं, तो उन पहचानकर्ताओं को उनके उपयोग से पहले घोषित करने की आवश्यकता है। यदि यह केंद्रीय शीर्षलेख फ़ाइल में किया जाता है, और आपको अतिरिक्त पहचानकर्ता जोड़ने की आवश्यकता है, तो बिल्ड टूल परिवर्तन का पता लगाएगा और इसमें शामिल प्रत्येक स्रोत फ़ाइल को पुन: संकलित करेगा। क्या आपकी निर्माण प्रक्रिया में इस हेडर फ़ाइल के लिए ऐसी सुविधा को निष्क्रिय करने के लिए संभव हो सकता है? – dyp

उत्तर

3

एक तरह से आप इस लक्ष्य को हासिल कर सकते हैं एक महत्वपूर्ण वर्ग है कि संख्यात्मक आईडी लपेटता है और जो सीधे instantiated नहीं किया जा सकता है, इसलिए संदर्भ के लिए मजबूर कर एक प्रकार सुरक्षित चर के माध्यम से किया जाना है इसके बाद के संस्करण, कुंजी की परिभाषा इस प्रकार दिखाई देगा:

// some_registration.h 
DECLARE_KEY(Value); 
DECLARE_KEY(AnotherValue); 
// ... 

// some_registration.cpp 
DEFINE_KEY(Value, 1); 
DEFINE_KEY(AnotherValue, 2); 
// ... 

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

उपयोग बहुत से पहले करने के समान होगा: इस प्रकार दिखाई देगा

GetValueFromDatabase(Value); 
AddValueToDatabase(Value, 5); 
int* temp = new int[keys::count()]; 

key.cpp फ़ाइल इसी यह पूरा करने के:

namespace keys { 
namespace { 
class KeyImpl : public Key { 
    public: 
    KeyImpl(const string& name, size_t id) : id_(id), name_(name) {} 
    ~KeyImpl() {} 
    virtual size_t id() const { return id_; } 
    virtual const std::string& name() const { return name_; } 

    private: 
    const size_t id_; 
    const std::string name_; 
}; 

class KeyList { 
    public: 
    KeyList() {} 
    ~KeyList() { 
     // This will happen only on program termination. We intentionally 
     // do not clean up "keys_" and just let this data get cleaned up 
     // when the entire process memory is deleted so that we do not 
     // cause existing references to keys to become dangling. 
    } 

    const Key& Add(const string& name, size_t id) { 
     ScopedLock lock(&mutex_); 
     if (id >= keys_.size()) { 
     keys_.resize(id + 1); 
     } 

     const Key* existing = keys_[id] 
     if (existing) { 
     if (existing->name() != name) { 
      // Potentially some sort of error handling 
      // or generation here... depending on the 
      // desired semantics, for example, below 
      // we use the Google Log library to emit 
      // a fatal error message and crash the program. 
      // This crash is expected to happen at start up. 
      LOG(FATAL) 
       << "Duplicate registration of key with ID " 
       << id << " seen while registering key named " 
       << "\"" << name << "\"; previously registered " 
       << "with name \"" << existing->name() << "\"."; 
     } 
     return *existing; 
     } 

     Key* result = new KeyImpl(name, id); 
     keys_[id] = result; 
     return *result; 
    } 

    size_t length() const { 
     ScopedLock lock(&mutex_); 
     return keys_.size(); 
    } 
    private: 
    std::vector<const Key*> keys_; 
    mutable Mutex mutex_; 
}; 

static LazyStaticPtr<KeysList> keys_list; 
} 

size_t count() { 
    return keys_list->length(); 
} 

namespace internal { 
    const Key& GetOrCreate(const std::string& name, size_t id) { 
    return keys_list->Add(name, id); 
    } 
} 
} 

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

+0

इस दृष्टिकोण में कुछ वादा है। मेरे पास थोड़ा सा झुकाव होगा और देखें कि क्या अन्य दृष्टिकोण हैं। – BlamKiwi

+2

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

+0

@dyp यही कारण है कि मैं ऐसे समाधान को देख रहा हूं जो समूह सूची को कम करने का वादा करता है जबकि अभी भी केंद्रीय सूची प्राप्त करने में सक्षम है। सूची की अखंडता को तब स्थिर स्थिरता द्वारा गारंटी दी जा सकती है। – BlamKiwi

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