C में स्ट्रिंग के लिए enum नाम कैसे बदलें


92

क्या सी में एन्यूमरेटर नाम को स्ट्रिंग में बदलने की संभावना है?

जवाबों:


184

एक तरीका है, प्रीप्रोसेसर बनाने का काम। यह भी सुनिश्चित करता है कि आपके एनम और तार सिंक में हैं।

#define FOREACH_FRUIT(FRUIT) \
        FRUIT(apple)   \
        FRUIT(orange)  \
        FRUIT(grape)   \
        FRUIT(banana)  \

#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,

enum FRUIT_ENUM {
    FOREACH_FRUIT(GENERATE_ENUM)
};

static const char *FRUIT_STRING[] = {
    FOREACH_FRUIT(GENERATE_STRING)
};

प्रीप्रोसेसर के हो जाने के बाद, आपके पास होगा:

enum FRUIT_ENUM {
    apple, orange, grape, banana,
};

static const char *FRUIT_STRING[] = {
    "apple", "orange", "grape", "banana",
};

तब आप कुछ ऐसा कर सकते थे:

printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);

यदि उपयोग का मामला शाब्दिक रूप से सिर्फ एनम नाम को प्रिंट कर रहा है, तो निम्न मैक्रोज़ जोड़ें:

#define str(x) #x
#define xstr(x) str(x)

फिर करो:

printf("enum apple as a string: %s\n", xstr(apple));

इस मामले में, ऐसा प्रतीत हो सकता है कि दो-स्तरीय मैक्रो अतिसुधार है, हालांकि, सी में कड़ाई से काम करने के कारण, कुछ मामलों में यह आवश्यक है। उदाहरण के लिए, मान लें कि हम एक Enum के साथ #define का उपयोग करना चाहते हैं:

#define foo apple

int main() {
    printf("%s\n", str(foo));
    printf("%s\n", xstr(foo));
}

उत्पादन होगा:

foo
apple

ऐसा इसलिए है क्योंकि str सेब को बढ़ाने के बजाय इनपुट foo को बढ़ाएगा। Xstr का उपयोग करके मैक्रो का विस्तार पहले किया जाता है, फिर उस परिणाम को कठोर किया जाता है।

अधिक जानकारी के लिए स्ट्रिंग देखें ।


1
यह एकदम सही है, लेकिन मैं यह समझने में असमर्थ हूं कि वास्तव में क्या हो रहा है। : O
p0lAris 23

यह भी कि उपरोक्त मामले में कोई स्ट्रिंग को एनम में कैसे परिवर्तित करता है?
p0lAris

वहाँ कुछ तरीके यह किया जा सकता है, आप क्या हासिल करने की कोशिश कर रहे हैं पर निर्भर करता है?
टेरेंस एम

5
यदि आप सेब और नारंगी के साथ नाम स्थान को प्रदूषित नहीं करना चाहते हैं ... तो आप इसके साथ उपसर्ग कर सकते हैं#define GENERATE_ENUM(ENUM) PREFIX##ENUM,
jaak

1
जो लोग इस पद पर आते हैं, उनके लिए एक कार्यक्रम में विभिन्न मदों को फिर से शुरू करने के लिए एक मैक्रो सूची का उपयोग करने का यह तरीका अनौपचारिक रूप से "एक्स 1 ड्रोन" कहलाता है।
लुंडिन

27

ऐसी स्थिति में जहां आपके पास यह है:

enum fruit {
    apple, 
    orange, 
    grape,
    banana,
    // etc.
};

मैं इसे हेडर फ़ाइल में रखना पसंद करता हूँ जहाँ एनम को परिभाषित किया गया है:

static inline char *stringFromFruit(enum fruit f)
{
    static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ };

    return strings[f];
}

4
मेरे जीवन के लिए मैं नहीं देख सकता कि यह कैसे मदद करता है। क्या आप इसे और अधिक स्पष्ट करने के लिए थोड़ा विस्तार कर सकते हैं।
डेविड हेफर्नन

2
ठीक है, यह कैसे मदद करता है? क्या आप कह रहे हैं कि टाइप करने की enumToString(apple)तुलना में टाइप करना आसान है "apple"? ऐसा नहीं है कि कहीं भी किसी भी प्रकार की सुरक्षा है। जब तक मुझे कुछ याद नहीं आ रहा है कि आप यहाँ क्या सुझाव दे रहे हैं व्यर्थ है और बस कोड को सफल बनाने में सफल होता है।
डेविड हेफर्नन

2
ठीक है, मैं अभी देखता हूं। मेरे विचार में मैक्रो फर्जी है और मेरा सुझाव है कि आप इसे हटा दें।
डेविड हेफर्नन

2
टिप्पणी मैक्रो के बारे में बात करते हैं। कहाँ है?
एमके ..

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

14

सीधे तौर पर इसे हासिल करने का कोई सरल तरीका नहीं है। लेकिन P99 में मैक्रोज़ हैं जो आपको इस प्रकार के फ़ंक्शन को स्वचालित रूप से बनाने की अनुमति देते हैं:

 P99_DECLARE_ENUM(color, red, green, blue);

एक हेडर फ़ाइल में, और

 P99_DEFINE_ENUM(color);

एक संकलन इकाई (.c फ़ाइल) में तब ट्रिक करनी चाहिए, उस उदाहरण में फ़ंक्शन तब कहा जाएगा color_getname


मैं इस देयता को कैसे खींचूं?
जॉनीटेक्स

14

मुझे एक सी प्रीप्रोसेसर चाल मिली जो एक समर्पित सरणी स्ट्रिंग (स्रोत: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_proprocessor_applications_en ) घोषित किए बिना एक ही काम कर रही है ।

अनुक्रमिक Enums

स्टीफन राम के आविष्कार के बाद, अनुक्रमिक एनम (बिना सूचकांक को स्पष्ट रूप से बताते हुए, जैसे enum {foo=-1, foo1 = 1}) इस प्रतिभा चाल की तरह महसूस किया जा सकता है:

#include <stdio.h>

#define NAMES C(RED)C(GREEN)C(BLUE)
#define C(x) x,
enum color { NAMES TOP };
#undef C

#define C(x) #x,    
const char * const color_name[] = { NAMES };

यह निम्न परिणाम देता है:

int main( void )  { 
    printf( "The color is %s.\n", color_name[ RED ]);  
    printf( "There are %d colors.\n", TOP ); 
}

रंग लाल है।
3 रंग हैं।

गैर-अनुक्रमिक एनम

चूँकि मैं एरर कोड परिभाषाओं को मैप करना चाहता था, इसलिए मैं एरर कोड को कच्ची कर सकता हूं, ताकि मैं एरर कोड में कच्ची एरर डेफिनिशन को जोड़ सकूं (उदाहरण के लिए "The error is 3 (LC_FT_DEVICE_NOT_OPENED)."), मैंने कोड को इस तरह से बढ़ाया कि आप संबंधित एनम वैल्यू के लिए आवश्यक इंडेक्स आसानी से निर्धारित कर सकें। :

#define LOOPN(n,a) LOOP##n(a)
#define LOOPF ,
#define LOOP2(a) a LOOPF a LOOPF
#define LOOP3(a) a LOOPF a LOOPF a LOOPF
#define LOOP4(a) a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP5(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP6(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP7(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP8(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP9(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF


#define LC_ERRORS_NAMES \
    Cn(LC_RESPONSE_PLUGIN_OK, -10) \
    Cw(8) \
    Cn(LC_RESPONSE_GENERIC_ERROR, -1) \
    Cn(LC_FT_OK, 0) \
    Ci(LC_FT_INVALID_HANDLE) \
    Ci(LC_FT_DEVICE_NOT_FOUND) \
    Ci(LC_FT_DEVICE_NOT_OPENED) \
    Ci(LC_FT_IO_ERROR) \
    Ci(LC_FT_INSUFFICIENT_RESOURCES) \
    Ci(LC_FT_INVALID_PARAMETER) \
    Ci(LC_FT_INVALID_BAUD_RATE) \
    Ci(LC_FT_DEVICE_NOT_OPENED_FOR_ERASE) \
    Ci(LC_FT_DEVICE_NOT_OPENED_FOR_WRITE) \
    Ci(LC_FT_FAILED_TO_WRITE_DEVICE) \
    Ci(LC_FT_EEPROM_READ_FAILED) \
    Ci(LC_FT_EEPROM_WRITE_FAILED) \
    Ci(LC_FT_EEPROM_ERASE_FAILED) \
    Ci(LC_FT_EEPROM_NOT_PRESENT) \
    Ci(LC_FT_EEPROM_NOT_PROGRAMMED) \
    Ci(LC_FT_INVALID_ARGS) \
    Ci(LC_FT_NOT_SUPPORTED) \
    Ci(LC_FT_OTHER_ERROR) \
    Ci(LC_FT_DEVICE_LIST_NOT_READY)


#define Cn(x,y) x=y,
#define Ci(x) x,
#define Cw(x)
enum LC_errors { LC_ERRORS_NAMES TOP };
#undef Cn
#undef Ci
#undef Cw
#define Cn(x,y) #x,
#define Ci(x) #x,
#define Cw(x) LOOPN(x,"")
static const char* __LC_errors__strings[] = { LC_ERRORS_NAMES };
static const char** LC_errors__strings = &__LC_errors__strings[10];

इस उदाहरण में, C प्रीप्रोसेसर निम्न कोड उत्पन्न करेगा :

enum LC_errors { LC_RESPONSE_PLUGIN_OK=-10,  LC_RESPONSE_GENERIC_ERROR=-1, LC_FT_OK=0, LC_FT_INVALID_HANDLE, LC_FT_DEVICE_NOT_FOUND, LC_FT_DEVICE_NOT_OPENED, LC_FT_IO_ERROR, LC_FT_INSUFFICIENT_RESOURCES, LC_FT_INVALID_PARAMETER, LC_FT_INVALID_BAUD_RATE, LC_FT_DEVICE_NOT_OPENED_FOR_ERASE, LC_FT_DEVICE_NOT_OPENED_FOR_WRITE, LC_FT_FAILED_TO_WRITE_DEVICE, LC_FT_EEPROM_READ_FAILED, LC_FT_EEPROM_WRITE_FAILED, LC_FT_EEPROM_ERASE_FAILED, LC_FT_EEPROM_NOT_PRESENT, LC_FT_EEPROM_NOT_PROGRAMMED, LC_FT_INVALID_ARGS, LC_FT_NOT_SUPPORTED, LC_FT_OTHER_ERROR, LC_FT_DEVICE_LIST_NOT_READY, TOP };

static const char* __LC_errors__strings[] = { "LC_RESPONSE_PLUGIN_OK", "" , "" , "" , "" , "" , "" , "" , "" "LC_RESPONSE_GENERIC_ERROR", "LC_FT_OK", "LC_FT_INVALID_HANDLE", "LC_FT_DEVICE_NOT_FOUND", "LC_FT_DEVICE_NOT_OPENED", "LC_FT_IO_ERROR", "LC_FT_INSUFFICIENT_RESOURCES", "LC_FT_INVALID_PARAMETER", "LC_FT_INVALID_BAUD_RATE", "LC_FT_DEVICE_NOT_OPENED_FOR_ERASE", "LC_FT_DEVICE_NOT_OPENED_FOR_WRITE", "LC_FT_FAILED_TO_WRITE_DEVICE", "LC_FT_EEPROM_READ_FAILED", "LC_FT_EEPROM_WRITE_FAILED", "LC_FT_EEPROM_ERASE_FAILED", "LC_FT_EEPROM_NOT_PRESENT", "LC_FT_EEPROM_NOT_PROGRAMMED", "LC_FT_INVALID_ARGS", "LC_FT_NOT_SUPPORTED", "LC_FT_OTHER_ERROR", "LC_FT_DEVICE_LIST_NOT_READY", };

यह निम्नलिखित कार्यान्वयन क्षमताओं का परिणाम है:

LC_errors__strings [-1] ==> LC_errors__strings [LC_RESPONSE_GENERIC_ERROR] ==> "LC_RESPONSE_GENERIC_ERROR"


अच्छा लगा। यह वही है जो मैं देख रहा था और इसके लिए उपयोग कर रहा था। वही त्रुटियां :)
mrbean

5

आपको यह सुनिश्चित करने की आवश्यकता नहीं है कि आपके एनम और स्ट्रिंग्स सिंक में हैं, यह सुनिश्चित करने के लिए आपको प्रीप्रोसेसर पर भरोसा करने की आवश्यकता नहीं है। मेरे लिए मैक्रोज़ का उपयोग कोड को पढ़ने के लिए कठिन बना देता है।

स्ट्रिंग्स का Enum और An Array का उपयोग करना

enum fruit                                                                   
{
    APPLE = 0, 
    ORANGE, 
    GRAPE,
    BANANA,
    /* etc. */
    FRUIT_MAX                                                                                                                
};   

const char * const fruit_str[] =
{
    [BANANA] = "banana",
    [ORANGE] = "orange",
    [GRAPE]  = "grape",
    [APPLE]  = "apple",
    /* etc. */  
};

ध्यान दें: fruit_strसरणी में तारों को एनम आइटम के समान क्रम में घोषित करने की आवश्यकता नहीं है।

इसे कैसे उपयोग करे

printf("enum apple as a string: %s\n", fruit_str[APPLE]);

एक संकलन समय जाँच जोड़ना

यदि आप एक स्ट्रिंग को भूल जाने से डरते हैं, तो आप निम्नलिखित चेक जोड़ सकते हैं:

#define ASSERT_ENUM_TO_STR(sarray, max) \                                       
  typedef char assert_sizeof_##max[(sizeof(sarray)/sizeof(sarray[0]) == (max)) ? 1 : -1]

ASSERT_ENUM_TO_STR(fruit_str, FRUIT_MAX);

यदि संकलित आइटम की मात्रा सरणी में तार की मात्रा से मेल नहीं खाती है, तो संकलन समय पर एक त्रुटि रिपोर्ट की जाएगी।


2

एनुम को वैध किए बिना ऐसा कार्य एक खतरनाक है। मैं एक स्विच स्टेटमेंट का उपयोग करने का सुझाव देता हूं। एक अन्य लाभ यह है कि इसका उपयोग उन मानदंड के लिए किया जा सकता है, जिन्होंने मूल्यों को परिभाषित किया है, उदाहरण के लिए झंडे के लिए जहां मान 1,2,4,8,16 आदि हैं।

अपनी सभी एनम स्ट्रिंग्स को एक साथ एक सरणी में रखें: -

static const char * allEnums[] = {
    "Undefined",
    "apple",
    "orange"
    /* etc */
};

हेडर फ़ाइल में सूचकांकों को परिभाषित करें: -

#define ID_undefined       0
#define ID_fruit_apple     1
#define ID_fruit_orange    2
/* etc */

ऐसा करने से विभिन्न संस्करणों का उत्पादन करना आसान हो जाता है, उदाहरण के लिए यदि आप अन्य भाषाओं के साथ अपने कार्यक्रम के अंतर्राष्ट्रीय संस्करण बनाना चाहते हैं।

मैक्रो का उपयोग करके, हेडर फ़ाइल में भी: -

#define CASE(type,val) case val: index = ID_##type##_##val; break;

एक स्विच स्टेटमेंट के साथ एक फंक्शन बनाएं, यह वापस आ जाना चाहिए const char *क्योंकि स्ट्रिंग्स स्टैटिक कॉस्ट: -

const char * FruitString(enum fruit e){

    unsigned int index;

    switch(e){
        CASE(fruit, apple)
        CASE(fruit, orange)
        CASE(fruit, banana)
        /* etc */
        default: index = ID_undefined;
    }
    return allEnums[index];
}

यदि विंडोज़ के साथ प्रोग्रामिंग की जाती है तो ID_ मान संसाधन मान हो सकते हैं।

(यदि C ++ का उपयोग कर रहे हैं तो सभी कार्यों का एक ही नाम हो सकता है।

string EnumToString(fruit e);

)


2

Hokyo के "नॉन-सेक्शनल एनुम्स" उत्तर का एक सरल विकल्प, स्ट्रिंगर को तुरंत करने के लिए डिज़ाइनर का उपयोग करने पर आधारित है:

#define NAMES C(RED, 10)C(GREEN, 20)C(BLUE, 30)
#define C(k, v) k = v,
enum color { NAMES };
#undef C

#define C(k, v) [v] = #k,    
const char * const color_name[] = { NAMES };

-2

मैं आमतौर पर ऐसा करता हूं:

#define COLOR_STR(color)                            \
    (RED       == color ? "red"    :                \
     (BLUE     == color ? "blue"   :                \
      (GREEN   == color ? "green"  :                \
       (YELLOW == color ? "yellow" : "unknown"))))   

1
यह बस हास्यास्पद है
मास्सिमो कैलेगारी

यह कोई बुरा जवाब नहीं है। यह स्पष्ट, सरल और समझने में आसान है। यदि आप उन प्रणालियों पर काम कर रहे हैं जहां अन्य लोगों को आपके कोड को जल्दी से पढ़ने और समझने की आवश्यकता है, तो स्पष्टता बहुत महत्वपूर्ण है। जब तक वे पूरी तरह से टिप्पणी नहीं करते हैं या कोडिंग मानक में वर्णित नहीं होते हैं, तब तक मैं प्रीप्रोसेसर चाल का उपयोग करने की सलाह नहीं दूंगा।
नील्सन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.