2010-04-16 19 views
11

में कनवर्ट करना संभव है मान लीजिए कि मेरे पास बाहरी पुस्तकालय के लिए हेडर फ़ाइल में #define s की एक सूची है। ये #define एस कार्यों से लौटाए गए त्रुटि कोड का प्रतिनिधित्व करते हैं। मैं एक रूपांतरण फ़ंक्शन लिखना चाहता हूं जो इनपुट के रूप में एक त्रुटि कोड ले सकता है और वास्तविक #define नाम का प्रतिनिधित्व करने वाले एक स्ट्रिंग शाब्दिक आउटपुट के रूप में वापस आ सकता है।#defines की स्ट्रिंग्स

उदाहरण के लिए, अगर मैं

#define NO_ERROR 0 
#define ONE_KIND_OF_ERROR 1 
#define ANOTHER_KIND_OF_ERROR 2 

मैं एक समारोह चाहते हैं की तरह

int errorCode = doSomeLibraryFunction(); 
if (errorCode) 
    writeToLog(convertToString(errorCode)); 

कहा जाता है और convertToString() करने में सक्षम हो है सक्षम होने के लिए किया जा रहा बिना कि त्रुटि कोड स्वत: परिवर्तित हो एक विशाल स्विच-केस

const char* convertToString(int errorCode) 
{ 
    switch (errorCode) 
    { 
     case NO_ERROR: 
      return "NO_ERROR"; 
     case ONE_KIND_OF_ERROR: 
      return "ONE_KIND_OF_ERROR"; 
     ... 
    ... 
... 

मुझे लगता है कि अगर टी उनका संभव है, यह टेम्पलेट्स और मेटाप्रोग्रामिंग का उपयोग करना संभव होगा, लेकिन यह केवल त्रुटि कोड काम करेगा, वास्तव में एक प्रकार था और प्रोसेसर मैक्रोज़ का गुच्छा नहीं था।

+0

क्षमा करें, मुझे कुछ जवाब देखने के बाद एक परिशिष्ट बनाना चाहिए: मैं जो करने में सक्षम होना चाहता हूं वह वास्तव में उन स्ट्रिंग्स की सूची संकलित करने और उसमें अनुक्रमणित करने के लिए उत्पन्न होता है। – brandonC

+0

का डुप्लिकेट: http://stackoverflow.com/questions/2571816/is-it-possible-to-define-enumalpha –

उत्तर

19

मैं सामान्य रूप से यह विशाल स्विच मामले तरीके से करना है, हालांकि मैं यह कुछ हद तक आसान के साथ करते हैं:

#define STR(code) case code: return #code 
switch (errorCode) 
{ 
    STR(NO_ERROR); 
    STR(ONE_KIND_OF_ERROR); 
} 

यह है एक अच्छा सवाल, मुझे इसमें रुचि देखने के लिए बेहतर तरीके से लोगों को क्या है

+0

+1 मैंने पहले कभी इस विधि के बारे में सोचा नहीं है। –

+0

मैं सुझाव देता हूं कि एसटीआर (या जो भी वास्तव में किया गया हो) के बजाय ऐसे मैक्रोज़ केस को कॉल करना। '#' स्ट्रिंग ऑपरेटर का उपयोग करने के लिए – hlovdal

+0

+1। –

4

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

+0

डाउनवोट का कारण? –

+0

लेकिन क्या कोई तरीका है कि निरंतर सरणी को कुछ कस्टम प्री-बिल्ड चरण पर लात मारने के बिना संकलन समय पर उत्पन्न किया जा सकता है? – brandonC

+0

मानक सी/सी ++ का उपयोग करके संकलन समय पर इसे उत्पन्न करने का कोई तरीका नहीं है। माइकल मोरहेक के 'एसटीआर (...)' उत्तर की तरह कुछ निकटतम आप प्राप्त कर सकते हैं। –

0

#define FOO 1 प्रीप्रोसेसर द्वारा एक साधारण पाठ प्रतिस्थापन के रूप में संभाला जाता है। यदि आप उन परिभाषाओं को संरक्षित करना चाहते हैं, तो आपको विशाल स्विच की आवश्यकता है।

6

उत्पन्न कोड में लोकप्रिय है कि यह करने के लिए एक और तरीका है:

#define NO_ERROR 0 
#define ONE_KIND_OF_ERROR 1 
#define ANOTHER_KIND_OF_ERROR 2 
static const char* const error_names[] = {"NO_ERROR", "ONE_KIND_OF_ERROR", "ANOTHER_KIND_OF_ERROR"}; 

const char* convertToString(int errorCode) {return error_names[errorCode];} 

मैं स्विच मामले तरह से मैं already mentioned पसंद करते हैं, लेकिन कैसे अपने कोड संरचित है यह अपने निर्माण की प्रक्रिया के हिस्से के रूप में आसान हो सकता है पर निर्भर करता है उस सरणी को स्वचालित रूप से उत्पन्न करने के लिए

+0

यह निश्चित रूप से एक अच्छा तरीका है, और स्विच-केस की तुलना में टाइपिंग पर बहुत आसान है। मैं चाहता हूं कि संकलन समय पर सरणी उत्पन्न करने का कोई तरीका था। – brandonC

2

बूस्ट प्रीप्रोसेसर पर एक नज़र डालें। आप कोड जोड़े की सूची बना सकते हैं/सरणी/अनुक्रम:

#define codes ((1,"code1"))((...)) 

#define code1 1 
... 

// then use preprocessor FOR_EACH to generate error handlers 

प्रासंगिक लिंक:

http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/seq_for_each.html

http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/tuple_elem.html

+2

बूस्ट प्रीप्रोसेसर का उपयोग करके कार्यान्वित एक समान प्रकार की समस्या का एक पूर्ण उदाहरण यहां दिया गया है: http://stackoverflow.com/questions/2576868/cc-enums-detect-when-multiple-items-map-to-same-value/2577102# 2577102 (यह शायद इसे संशोधित करने के लिए _too_ अधिक काम नहीं करेगा)

+0

@ जेम्स धन्यवाद, मैं अधिक पूर्ण लिस्टिंग प्रदान करने के लिए बहुत आलसी था। – Anycorn

+0

यह वास्तव में एक अच्छा समाधान है, और निश्चित रूप से जो कुछ मैं ढूंढ रहा था उसके करीब कुछ। मुझे नहीं लगता कि यह मेरी विशेष स्थिति में काम करेगा, एचएच फाइल को नियंत्रित नहीं करेगा और सभी, लेकिन निश्चित रूप से मुझे इस मामले में – brandonC

1

यहां संभावना एक छोटे से प्रोग्राम है जो पार्स करता है लिखने के लिए है। एच फ़ाइल जिसमें # परिभाषाएं हैं, और convertToString() फ़ंक्शन के लिए संबंधित स्रोत कोड उत्सर्जित करती हैं। जब भी .h फ़ाइल बदल जाती है तो आप उस प्रोग्राम को स्वचालित रूप से अपनी बिल्ड प्रक्रिया के हिस्से के रूप में चला सकते हैं। यह थोड़ा और आगे काम करता है, लेकिन इसे लागू करने के बाद आपको कभी भी अपने int < -> स्ट्रिंग रूपांतरण फ़ंक्शन को मैन्युअल रूप से अपडेट करने की आवश्यकता नहीं होगी।

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

+0

मैं इसके साथ सहमत होगा। हालांकि, मेरी आशा पूरी तरह से सी/सी ++ प्रीप्रोसेसर के भीतर उस पीढ़ी को करने में सक्षम थी। कोड पीढ़ी करने के लिए एक कस्टम प्री-बिल्ड टूल लिखने के साथ मेरा सबसे बड़ा आरक्षण यह है कि मैं वास्तव में उपयोग से अधिक होने के कारण कोड-जेन टूल लिखने और बनाए रखने में अधिक समय व्यतीत करूंगा, खासकर जब से '# परिभाषा' बाहरी पुस्तकालय का हिस्सा, मुझे आशा है कि वे बदलने की संभावना नहीं होगी। – brandonC

1

यदि आप निश्चित रूप से #define रखना चाहते हैं तो मैं माइकल के सुरुचिपूर्ण #define STR(code) उत्तर के साथ जाऊंगा। लेकिन परिभाषित सी ++ से अधिक सी है, और परिभाषित करने के लिए बड़ी नकारात्मकता यह है कि आप उन्हें नामस्थान में नहीं डाल सकते हैं। वे किसी भी कार्यक्रम आप उन्हें में शामिल की वैश्विक नामस्थान को दूषित होगा अगर यह आपकी सत्ता में है इसे बदलने के लिए, मैं बजाय एक गुमनाम enum का उपयोग कर की सिफारिश करेंगे:।

enum{ NO_ERROR ONE_KIND_OF_ERROR ANOTHER_KIND_OF_ERROR } 

यह ठीक #define रों आपके पास करने के समान है , और आप इसे एक नामस्थान में डाल सकते हैं। और अब आप माइकल के दूसरे उत्तर का उपयोग static const char* const error_names सरणी से कर सकते हैं जो आपने मूल रूप से पूछा था।

+1

यह एक अच्छा जवाब है, और निश्चित रूप से मैं क्या करूंगा यदि मैं वास्तव में हेडर फ़ाइलों को नियंत्रित करता हूं, लेकिन दुर्भाग्य से इन्हें बाहरी पुस्तकालय के हिस्से के रूप में प्रदान किया जाता है, इसलिए # डिफाइन को enums में बदलना, जबकि बेहतर, वास्तव में एक विकल्प नहीं है फिर एक भयानक समाधान है, अगर मैं मैंने सोचा कि मामला था ओह अच्छा – brandonC

+0

@Goose, मैं इसे इस तरह से सब साथ कर दिया गया है स्विच के साथ एक नए परिप्रेक्ष्य मिल गया हालांकि – rocknroll

+0

@brandonC।।।। एक लाइब्रेरी लिख रहा था और त्रुटि कोडों को नियंत्रित कर रहा था। दुर्भाग्यवश, किसी और ने मुझे पहले से ही लाइब्रेरी प्रदान की है जो # परिभाषित इनट्स के माध्यम से त्रुटि कोड वापस करने का विकल्प चुनती है। ओह ठीक है, मुझे लगा कि मैं जो चाहता था शायद संभव नहीं था। धन्यवाद हालांकि, जानकारीपूर्ण उत्तर के लिए। –

1

आप वास्तव में इसे दोनों तरीकों से प्राप्त कर सकते हैं, यानी कोड से अनुवाद करने के लिए दो फ़ंक्शन हैं।

पाठ्यक्रम की पहली बात यह है कि #define का उपयोग स्थिरांक के लिए नहीं किया जाना चाहिए, एक enum शायद सबसे अच्छा होगा, हालांकि एक enum विस्तारित नहीं किया जा सकता है, जिसके लिए आपकी सभी त्रुटियों को एक ही स्थान पर परिभाषित किया जाना चाहिए (ouch, धन्यवाद निर्भरताओं के लिए बहुत कुछ ...)

हालांकि आप इसे अन्य कर सकते हैं, नामों को अलग करने के लिए नामस्थानों का उपयोग करके, और पूरी पीढ़ी को संभालने के लिए प्रीप्रोकैसिंग। लुकअप भाग के लिए, हम Bimap का उपयोग करेंगे।

पहले हम एक हैंडलर वर्ग को परिभाषित करने की जरूरत है (जो भी इनलाइन के लिए आवश्यक नहीं है, लेकिन यह उदाहरण के लिए आसान है)

#include <boost/bimap.hpp> 
#include <boost/optional.hpp> 

namespace error 
{ 

    class Handler 
    { 
    public: 
    typedef boost::optional<int> return_code; 
    typedef boost::optional<std::string> return_description; 

    static bool Register(int code, const char* description) 
    { 
     typedef error_map::value_type value_type; 
     bool result = MMap().insert(value_type(code,description)).second; 

     // assert(result && description) 
     return result; 
    } 

    static return_code GetCode(std::string const& desc) 
    { 
     error_map::map_by<description>::const_iterator it 
      = MMap().by<description>().find(desc); 
     if (it != MMap().by<description>().end()) return it->second; 
     else return return_code(); 
    } 

    static return_description GetDescription(int c) 
    { 
     error_map::map_by<code>::const_iterator it 
      = MMap().by<code>().find(c); 
     if (it != MMap().by<code>().end()) return it->second; 
     else return return_description(); 
    } 

    typedef std::vector< std::pair<int,std::string> > errors_t; 
    static errors_t GetAll() 
    { 
     errors_t result; 
     std::for_each(MMap().left.begin(), MMap().left.end(), 
        result.push_back(boost::lambda::_1)); 
     return result; 
    } 

    private: 
    struct code {}; 
    struct description {}; 

    typedef boost::bimap< 
     boost::tagged<int, code>, 
     boost::tagged<std::string, description> 
    > error_map; 

    static error_map& Map() { static error_map MMap; return MMap; } 
    }; 

    // Short-Hand 
    boost::optional<int> GetCode(std::string const& d) 
    { 
    return Handler::GetCode(d); 
    } 

    boost::optional<std::string> GetDescription(int c) 
    { 
    return Handler::GetDescription(c); 
    } 
} // namespace error 

तो हम बस कुछ वाक्यात्मक चीनी उपलब्ध कराने की आवश्यकता:

#define DEFINE_NEW_ERROR(Code_, Description_)   \ 
    const int Description_ = Code_;      \ 
    namespace error {          \ 
    const bool Description##_Registered =    \ 
     ::error::Handler::Register(Code_, #Description_); \ 
    } 

अज्ञात त्रुटि के पंजीकरण के मामले में हम थोड़ा अधिक हिंसक हो सकते हैं (उदाहरण के लिए जोर दें)।

और फिर हम हमेशा उस मैक्रो को उसमें लपेट सकते हैं जो एक से अधिक प्रतीकों को परिभाषित कर सकता है ... लेकिन यह एक अभ्यास के रूप में छोड़ा गया है।

उपयोग:

// someErrors.hpp 
#include "error/handler.hpp" 

DEFINE_NEW_ERROR(1, AnError) 
DEFINE_NEW_ERROR(2, AnotherError) 

// someFile.cpp 
#include <iostream> 
#include "error/handler.hpp" 

int main(int argc, char* argv[]) 
{ 
    int code = 6; 
    boost::optional<std::string> desc = error::GetDescription(code); 

    if (desc) 
    { 
    std::cout << "Code " << code << " is mapped to <" << *desc << ">" << std::endl; 
    } 
    else 
    { 
    std::cout << "Code " << code << " is unknown, here is the list:\n"; 

    ::error::Handler::errors_t errors = ::Error::Handler::GetAll(); 

    std::for_each(errors.begin(), errors.end(), std::cout << " " << _1); 

    std::cout << std::endl; 
    } 
} 

अस्वीकरण: मैं लैम्ब्डा सिंटैक्स के बारे में भी यकीन नहीं है, लेकिन यह लेखन को सरल बनाया था।

+0

,: इस मामले में :( – brandonC

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