जवाबों:
एक तरीका है, प्रीप्रोसेसर बनाने का काम। यह भी सुनिश्चित करता है कि आपके एनम और तार सिंक में हैं।
#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 का उपयोग करके मैक्रो का विस्तार पहले किया जाता है, फिर उस परिणाम को कठोर किया जाता है।
अधिक जानकारी के लिए स्ट्रिंग देखें ।
#define GENERATE_ENUM(ENUM) PREFIX##ENUM,
ऐसी स्थिति में जहां आपके पास यह है:
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];
}
enumToString(apple)
तुलना में टाइप करना आसान है "apple"
? ऐसा नहीं है कि कहीं भी किसी भी प्रकार की सुरक्षा है। जब तक मुझे कुछ याद नहीं आ रहा है कि आप यहाँ क्या सुझाव दे रहे हैं व्यर्थ है और बस कोड को सफल बनाने में सफल होता है।
सीधे तौर पर इसे हासिल करने का कोई सरल तरीका नहीं है। लेकिन P99 में मैक्रोज़ हैं जो आपको इस प्रकार के फ़ंक्शन को स्वचालित रूप से बनाने की अनुमति देते हैं:
P99_DECLARE_ENUM(color, red, green, blue);
एक हेडर फ़ाइल में, और
P99_DEFINE_ENUM(color);
एक संकलन इकाई (.c फ़ाइल) में तब ट्रिक करनी चाहिए, उस उदाहरण में फ़ंक्शन तब कहा जाएगा color_getname
।
मुझे एक सी प्रीप्रोसेसर चाल मिली जो एक समर्पित सरणी स्ट्रिंग (स्रोत: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_proprocessor_applications_en ) घोषित किए बिना एक ही काम कर रही है ।
स्टीफन राम के आविष्कार के बाद, अनुक्रमिक एनम (बिना सूचकांक को स्पष्ट रूप से बताते हुए, जैसे 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"
आपको यह सुनिश्चित करने की आवश्यकता नहीं है कि आपके एनम और स्ट्रिंग्स सिंक में हैं, यह सुनिश्चित करने के लिए आपको प्रीप्रोसेसर पर भरोसा करने की आवश्यकता नहीं है। मेरे लिए मैक्रोज़ का उपयोग कोड को पढ़ने के लिए कठिन बना देता है।
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);
यदि संकलित आइटम की मात्रा सरणी में तार की मात्रा से मेल नहीं खाती है, तो संकलन समय पर एक त्रुटि रिपोर्ट की जाएगी।
एनुम को वैध किए बिना ऐसा कार्य एक खतरनाक है। मैं एक स्विच स्टेटमेंट का उपयोग करने का सुझाव देता हूं। एक अन्य लाभ यह है कि इसका उपयोग उन मानदंड के लिए किया जा सकता है, जिन्होंने मूल्यों को परिभाषित किया है, उदाहरण के लिए झंडे के लिए जहां मान 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);
)
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 };
मैं आमतौर पर ऐसा करता हूं:
#define COLOR_STR(color) \
(RED == color ? "red" : \
(BLUE == color ? "blue" : \
(GREEN == color ? "green" : \
(YELLOW == color ? "yellow" : "unknown"))))