सी में स्ट्रिंग के रूप में एनम प्रकारों के चर का उपयोग करने का आसान तरीका?


87

यहाँ मैं क्या करने की कोशिश कर रहा हूँ:

typedef enum { ONE, TWO, THREE } Numbers;

मैं एक फ़ंक्शन लिखने की कोशिश कर रहा हूं जो निम्नलिखित के समान स्विच केस करेगा:

char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, num); //some way to get the symbolic constant name in here?
    } break;
    default:
      return 0; //no match
  return 1;
}

हर मामले में परिभाषित करने के बजाय, एनम चर का उपयोग करके इसे सेट करने का एक तरीका है जैसे मैं ऊपर करने की कोशिश कर रहा हूं?

जवाबों:


14

कोई अंतर्निहित समाधान नहीं है। सबसे आसान तरीका उस सरणी के साथ है char*जहाँ Enum का int मान उस Enum के वर्णनात्मक नाम वाले स्ट्रिंग को अनुक्रमित करता है। यदि आपके पास एक स्पार्स है enum(एक जो 0 पर शुरू नहीं होता है या नंबरिंग में अंतराल है) जहां कुछ intमैपिंग एक सरणी-आधारित मैपिंग को अव्यवहारिक बनाने के लिए पर्याप्त हैं, तो आप इसके बजाय एक हैश तालिका का उपयोग कर सकते हैं।


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

70

एक सी पहचानकर्ता और एक स्ट्रिंग दोनों कुछ बनाने से तकनीक ? यहाँ इस्तेमाल किया जा सकता है।

हमेशा की तरह इस तरह के प्रीप्रोसेसर सामान के साथ, प्रीप्रोसेसर भाग को लिखना और समझना कठिन हो सकता है, और इसमें मैक्रोज़ को अन्य मैक्रोज़ में शामिल करना और # और ## ऑपरेटरों का उपयोग करना शामिल है, लेकिन इसका उपयोग करना आसान है। मुझे यह शैली लंबे समय के लिए बहुत उपयोगी लगती है, जहां एक ही सूची को दो बार बनाए रखने से वास्तव में परेशानी हो सकती है।

फैक्टरी कोड - केवल एक बार टाइप किया जाता है, आमतौर पर हेडर में छिपा होता है:

enumFactory.h:

// expansion macro for enum value definition
#define ENUM_VALUE(name,assign) name assign,

// expansion macro for enum to string conversion
#define ENUM_CASE(name,assign) case name: return #name;

// expansion macro for string to enum conversion
#define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name;

/// declare the access function and define enum values
#define DECLARE_ENUM(EnumType,ENUM_DEF) \
  enum EnumType { \
    ENUM_DEF(ENUM_VALUE) \
  }; \
  const char *GetString(EnumType dummy); \
  EnumType Get##EnumType##Value(const char *string); \

/// define the access function names
#define DEFINE_ENUM(EnumType,ENUM_DEF) \
  const char *GetString(EnumType value) \
  { \
    switch(value) \
    { \
      ENUM_DEF(ENUM_CASE) \
      default: return ""; /* handle input error */ \
    } \
  } \
  EnumType Get##EnumType##Value(const char *str) \
  { \
    ENUM_DEF(ENUM_STRCMP) \
    return (EnumType)0; /* handle input error */ \
  } \

कारखाने का इस्तेमाल किया

someEnum.h:

#include "enumFactory.h"
#define SOME_ENUM(XX) \
    XX(FirstValue,) \
    XX(SecondValue,) \
    XX(SomeOtherValue,=50) \
    XX(OneMoreValue,=100) \

DECLARE_ENUM(SomeEnum,SOME_ENUM)

someEnum.cpp:

#include "someEnum.h"
DEFINE_ENUM(SomeEnum,SOME_ENUM)

तकनीक को आसानी से बढ़ाया जा सकता है ताकि XX मैक्रोज़ अधिक तर्कों को स्वीकार कर ले, और आप अलग-अलग जरूरतों के लिए XX के विकल्प के लिए अधिक मैक्रोज़ भी तैयार कर सकते हैं, इस नमूने में मैंने जो तीन प्रदान किए हैं, उनके समान।

#Include / #define / #undef का उपयोग करके X-Macros की तुलना

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


2
मुझे यकीन है कि आप कैसे कह सकते हैं यह बेहतर है नहीं कर रहा हूँ / से एक्स-मैक्रो बदतर - यह है एक्स मैक्रो। यह SOME_ENUM(XX)वास्तव में एक एक्स-मैक्रो है (सटीक होने के लिए, "उपयोगकर्ता प्रपत्र" जो XXफ़ंक्शन का उपयोग करने के बजाय पास करता है #def #undef) और फिर बदले में पूरे एक्स-मैक्रो को तब DEFINE_ENUM को दिया जाता है जो इसका उपयोग करता है। समाधान से कुछ भी दूर नहीं ले जाना - यह अच्छी तरह से काम करता है। बस यह स्पष्ट करने के लिए कि यह एक्स मैक्रोज़ का उपयोग है।
मधुमक्खी पालन

1
@BeeOnRope आपके द्वारा नोट किया गया अंतर महत्वपूर्ण है, और इस समाधान को मुहावरेदार एक्स मैक्रोज़ (जैसे विकिपीडिया के उदाहरण ) से अलग करता है। XXरी- #defineआईएनजी से गुजरने का लाभ यह है कि पूर्व पैटर्न का उपयोग मैक्रो विस्तार में किया जा सकता है। ध्यान दें कि संक्षिप्त रूप में अन्य समाधानों के रूप में यह सब एक नई पहेली को परिभाषित करने के लिए एक अलग फ़ाइल के निर्माण और कई-समावेश की आवश्यकता है।
चतुर्वर्ग

1
एक और ट्रिक है एनम नाम को मैक्रो नाम के रूप में इस्तेमाल करना । आप बस लिख सकते हैं #define DEFINE_ENUM(EnumType) ..., के ENUM_DEF(...)साथ बदल सकते हैं EnumType(...), और उपयोगकर्ता को कह सकते हैं #define SomeEnum(XX) ...। सी प्रीप्रोसेसर को SomeEnumकोष्ठक में और फिर एक नियमित टोकन में अन्यथा स्थूल आह्वान में प्रासंगिक रूप से विस्तारित किया जाएगा । (बेशक, इस समस्याओं उपयोगकर्ता का उपयोग करने के लिए पसंद करती है, तो कारण बनता है SomeEnum(2)के बजाय enum प्रकार के कलाकारों के लिए (SomeEnum)2या static_cast<SomeEnum>(2)।)
pmttavara

1
@pmttavara - यकीन है, अगर एक त्वरित खोज किसी भी संकेत है x-macros का सबसे आम उपयोग एक निश्चित आंतरिक-मैक्रो नाम के साथ #defineऔर उपयोग करता है #undef। क्या आप इस बात से असहमत हैं कि "उपयोगकर्ता फ़ॉर्म" (उदाहरण के लिए, इस आलेख के निचले भाग में सुझाया गया ) एक प्रकार का x-मैक्रो है? मैंने निश्चित रूप से इसे हमेशा एक एक्स-मैक्रो कहा है और सी कोडबेस में मैं हाल ही में यह सबसे सामान्य रूप है (यह स्पष्ट रूप से एक पक्षपाती अवलोकन है)। मैं हालांकि गलत ओपी को पार्स कर रहा हूं।
मधुमक्खी पालन

2
@BeeOnRope वर्तमान शब्द संपादन का एक परिणाम है, जैसा कि आपने मुझे वापस आश्वस्त किया है, तो यह x-मैक्रो है, भले ही यह शायद कम इस्तेमाल किया गया रूप था (या कम से कम एक लेख में उल्लेख किया गया था)।
सुमा

62
// Define your enumeration like this (in say numbers.h);
ENUM_BEGIN( Numbers )
    ENUM(ONE),
    ENUM(TWO),
    ENUM(FOUR)
ENUM_END( Numbers )

// The macros are defined in a more fundamental .h file (say defs.h);
#define ENUM_BEGIN(typ) enum typ {
#define ENUM(nam) nam
#define ENUM_END(typ) };

// Now in one and only one .c file, redefine the ENUM macros and reinclude
//  the numbers.h file to build a string table
#undef ENUM_BEGIN
#undef ENUM
#undef ENUM_END
#define ENUM_BEGIN(typ) const char * typ ## _name_table [] = {
#define ENUM(nam) #nam
#define ENUM_END(typ) };
#undef NUMBERS_H_INCLUDED   // whatever you need to do to enable reinclusion
#include "numbers.h"

// Now you can do exactly what you want to do, with no retyping, and for any
//  number of enumerated types defined with the ENUM macro family
//  Your code follows;
char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO"
    } break;
    default:
      return 0; //no match
  return 1;
}

// Sweet no ? After being frustrated by this for years, I finally came up
//  with this solution for my most recent project and plan to reuse the idea
//  forever

3
इस तरह की बात के लिए cpp बनाया गया था। +1।
डेरिक तुर्क

6
यह एक अच्छा जवाब है, यह विशेष उपकरणों का उपयोग किए बिना सबसे अच्छा एक के बारे में प्रतीत होता है, और मैंने इस तरह का काम पहले भी किया है; लेकिन यह अभी भी कभी भी 'सही' नहीं लगता है और मुझे यह करना कभी पसंद नहीं है ...
माइकल बूर

छोटे परिवर्तन: #define ENUM_END(typ) }; extern const char * typ ## _name_table[];में defs.hफ़ाइल - इस फाइल को आप इसका इस्तेमाल में अपना नाम तालिका घोषित करेंगे। (तालिका आकार घोषित करने के लिए एक अच्छा तरीका समझ नहीं सकते, हालांकि।) इसके अलावा, व्यक्तिगत रूप से मैं अंतिम अर्धविराम को छोड़ दूंगा, लेकिन गुण किसी भी तरह से बहस करने योग्य हैं।
क्रिस लुट्ज़

1
@ बिल, typलाइन में क्यों परेशान #define ENUM_END(typ) };?
पचेरियर

यह वह जगह नहीं है जहाँ मैं चाहता हूँ कि मेरे मैक्रो को "ONE = 5" के रूप में परिभाषित किया जाए
UKMonkey

13

ऐसा करने का एक तरीका निश्चित रूप से है - एक्स () मैक्रोज़ का उपयोग करें । ये मैक्रो स्रोत डेटा की सूची से एनम, एरे और कोड ब्लॉक बनाने के लिए सी प्रीप्रोसेसर का उपयोग करते हैं। आपको केवल X () मैक्रो वाले #define में नए आइटम जोड़ने की आवश्यकता है। स्विच स्टेटमेंट अपने आप विस्तृत हो जाएगा।

आपका उदाहरण इस प्रकार लिखा जा सकता है:

 // Source data -- Enum, String
 #define X_NUMBERS \
    X(ONE,   "one") \
    X(TWO,   "two") \
    X(THREE, "three")

 ...

 // Use preprocessor to create the Enum
 typedef enum {
  #define X(Enum, String)       Enum,
   X_NUMBERS
  #undef X
 } Numbers;

 ...

 // Use Preprocessor to expand data into switch statement cases
 switch(num)
 {
 #define X(Enum, String) \
     case Enum:  strcpy(num_str, String); break;
 X_NUMBERS
 #undef X

     default: return 0; break;
 }
 return 1;

अधिक कुशल तरीके हैं (यानी एक स्ट्रिंग सरणी और एनम इंडेक्स बनाने के लिए एक्स मैक्रोज़ का उपयोग करना), लेकिन यह सबसे सरल डेमो है।


8

मुझे पता है कि आपके पास कुछ अच्छे ठोस उत्तर हैं, लेकिन क्या आप सी प्रीप्रोसेसर में # ऑपरेटर के बारे में जानते हैं?

यह आपको ऐसा करने देता है:

#define MACROSTR(k) #k

typedef enum {
    kZero,
    kOne,
    kTwo,
    kThree
} kConst;

static char *kConstStr[] = {
    MACROSTR(kZero),
    MACROSTR(kOne),
    MACROSTR(kTwo),
    MACROSTR(kThree)
};

static void kConstPrinter(kConst k)
{
    printf("%s", kConstStr[k]);
}

char const *kConstStr[]
ऐनी वैन रोसुम

6

C या C ++ यह कार्यक्षमता प्रदान नहीं करता है, हालांकि मुझे इसकी अक्सर आवश्यकता होती है।

निम्नलिखित कोड काम करता है, हालांकि यह गैर-विरल Enums के लिए सबसे उपयुक्त है।

typedef enum { ONE, TWO, THREE } Numbers;
char *strNumbers[] = {"one","two","three"};
printf ("Value for TWO is %s\n",strNumbers[TWO]);

गैर-विरल द्वारा, मेरा मतलब है कि फॉर्म का नहीं

typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;

चूँकि इसमें भारी अंतराल हैं।

इस पद्धति का लाभ यह है कि यह एक-दूसरे के पास की दुश्मनी और तार की परिभाषाएं डालती है; एक समारोह में एक स्विच स्टेटमेंट होने के कारण उन्हें भाला लगता है। इसका मतलब है कि आप एक के बिना दूसरे को बदलने की संभावना कम है।


6

चुम्मा। आप अपने enums के साथ अन्य सभी प्रकार के स्विच / केस चीजें कर रहे होंगे, इसलिए मुद्रण अलग क्यों होना चाहिए? जब आप विचार करते हैं कि आप एक मामले को भूल सकते हैं तो लगभग 100 अन्य स्थानों पर विचार करने पर आपके प्रिंट रूटीन में किसी मामले को भूल जाना कोई बड़ी बात नहीं है। बस संकलित करें, जो गैर-थकाऊ केस मैचों की चेतावनी देगा। "डिफ़ॉल्ट" का उपयोग न करें क्योंकि यह स्विच को संपूर्ण बना देगा और आपको चेतावनी नहीं मिलेगी। इसके बजाय, स्विच से बाहर निकलने दें और डिफ़ॉल्ट मामले से निपटें जैसे ...

const char *myenum_str(myenum e)
{
    switch(e) {
    case ONE: return "one";
    case TWO: return "two";
    }
    return "invalid";
}


4

बढ़ावा देने का उपयोग :: प्रीप्रोसेसर निम्नलिखित की तरह एक सुंदर समाधान संभव बनाता है:

चरण 1: शीर्ष लेख फ़ाइल शामिल करें:

#include "EnumUtilities.h"

चरण 2: निम्नलिखित सिंटैक्स के साथ गणना वस्तु घोषित करें:

MakeEnum( TestData,
         (x)
         (y)
         (z)
         );

चरण 3: अपने डेटा का उपयोग करें:

तत्वों की संख्या प्राप्त करना:

td::cout << "Number of Elements: " << TestDataCount << std::endl;

संबंधित स्ट्रिंग प्राप्त करना:

std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;

संबंधित स्ट्रिंग से एनम मान प्राप्त करना:

std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
std::cout << "Value of z is " << TestData2Enum("z") << std::endl;

यह साफ और कॉम्पैक्ट दिखता है, जिसमें कोई अतिरिक्त फाइल शामिल नहीं है। EnumUtilities.h के भीतर मैंने जो कोड लिखा था वह निम्नलिखित है:

#include <boost/preprocessor/seq/for_each.hpp>
#include <string>

#define REALLY_MAKE_STRING(x) #x
#define MAKE_STRING(x) REALLY_MAKE_STRING(x)
#define MACRO1(r, data, elem) elem,
#define MACRO1_STRING(r, data, elem)    case elem: return REALLY_MAKE_STRING(elem);
#define MACRO1_ENUM(r, data, elem)      if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;


#define MakeEnum(eName, SEQ) \
    enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
    last_##eName##_enum}; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    }; \
    static enum eName eName##2Enum(const std::string eStrEl) \
    { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
        return (enum eName)0; \
    };

कुछ सीमाएं हैं, अर्थात बढ़ावा देने वाले :: प्रीप्रोसेसर। इस स्थिति में, स्थिरांक की सूची 64 तत्वों से बड़ी नहीं हो सकती।

उसी तर्क के बाद, आप विरल एनम बनाने के लिए भी सोच सकते हैं:

#define EnumName(Tuple)                 BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
#define EnumValue(Tuple)                BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
#define MACRO2(r, data, elem)           EnumName(elem) EnumValue(elem),
#define MACRO2_STRING(r, data, elem)    case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));

#define MakeEnumEx(eName, SEQ) \
    enum eName { \
    BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
    last_##eName##_enum }; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    };  

इस मामले में, वाक्यविन्यास है:

MakeEnumEx(TestEnum,
           ((x,))
           ((y,=1000))
           ((z,))
           );

उपयोग ऊपर के समान है (शून्य से ## 2Enum फ़ंक्शन को घटाएं, ताकि आप पिछले सिंटैक्स से एक्सट्रपलेशन करने की कोशिश कर सकें)।

मैंने इसे मैक और लिनक्स पर परीक्षण किया, लेकिन ध्यान रखें कि बढ़ावा :: प्रीप्रोसेसर पूरी तरह से पोर्टेबल नहीं हो सकता है।


3

यहाँ पर कुछ तकनीकों का विलय करके मैं सबसे सरल रूप में आया:

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}

2

यदि आप gcc का उपयोग कर रहे हैं, तो इसका उपयोग करना संभव है:

const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};

तो बस उदाहरण के लिए कॉल करें

enum_to_string_map[enum1]

1

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

हैडर: पेस्ट 1। एच

/*
@(#)File:           $RCSfile: paste1.h,v $
@(#)Version:        $Revision: 1.1 $
@(#)Last changed:   $Date: 2008/05/17 21:38:05 $
@(#)Purpose:        Automated Token Pasting
*/

#ifndef JLSS_ID_PASTE_H
#define JLSS_ID_PASTE_H

/*
 * Common case when someone just includes this file.  In this case,
 * they just get the various E* tokens as good old enums.
 */
#if !defined(ETYPE)
#define ETYPE(val, desc) E##val,
#define ETYPE_ENUM
enum {
#endif /* ETYPE */

   ETYPE(PERM,  "Operation not permitted")
   ETYPE(NOENT, "No such file or directory")
   ETYPE(SRCH,  "No such process")
   ETYPE(INTR,  "Interrupted system call")
   ETYPE(IO,    "I/O error")
   ETYPE(NXIO,  "No such device or address")
   ETYPE(2BIG,  "Arg list too long")

/*
 * Close up the enum block in the common case of someone including
 * this file.
 */
#if defined(ETYPE_ENUM)
#undef ETYPE_ENUM
#undef ETYPE
ETYPE_MAX
};
#endif /* ETYPE_ENUM */

#endif /* JLSS_ID_PASTE_H */

उदाहरण स्रोत:

/*
@(#)File:           $RCSfile: paste1.c,v $
@(#)Version:        $Revision: 1.2 $
@(#)Last changed:   $Date: 2008/06/24 01:03:38 $
@(#)Purpose:        Automated Token Pasting
*/

#include "paste1.h"

static const char *sys_errlist_internal[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) desc,
#include "paste1.h"
    0
#undef ETYPE
};

static const char *xerror(int err)
{
    if (err >= ETYPE_MAX || err <= 0)
        return "Unknown error";
    return sys_errlist_internal[err];
}

static const char*errlist_mnemonics[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) [E ## val] = "E" #val,
#include "paste1.h"
#undef ETYPE
};

#include <stdio.h>

int main(void)
{
    int i;

    for (i = 0; i < ETYPE_MAX; i++)
    {
        printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i));
    }
    return(0);
}

जरूरी नहीं कि सी-प्री-प्रोसेसर का दुनिया का सबसे साफ उपयोग हो - लेकिन यह कई बार सामग्री को लिखने से रोकता है।



0

यदि एनम इंडेक्स 0-आधारित है, तो आप नामों को चार * की एक सरणी में रख सकते हैं, और उन्हें एनम मूल्य के साथ इंडेक्स कर सकते हैं।



0

मैं एक साधारण टेम्प्लेट वर्ग बनाया है streamable_enumकि का उपयोग करता है ऑपरेटरों स्ट्रीम <<और >>और पर आधारित है std::map<Enum, std::string>:

#ifndef STREAMABLE_ENUM_HPP
#define STREAMABLE_ENUM_HPP

#include <iostream>
#include <string>
#include <map>

template <typename E>
class streamable_enum
{
public:
    typedef typename std::map<E, std::string> tostr_map_t;
    typedef typename std::map<std::string, E> fromstr_map_t;

    streamable_enum()
    {}

    streamable_enum(E val) :
        Val_(val)
    {}

    operator E() {
        return Val_;
    }

    bool operator==(const streamable_enum<E>& e) {
        return this->Val_ == e.Val_;
    }

    bool operator==(const E& e) {
        return this->Val_ == e;
    }

    static const tostr_map_t& to_string_map() {
        static tostr_map_t to_str_(get_enum_strings<E>());
        return to_str_;
    }

    static const fromstr_map_t& from_string_map() {
        static fromstr_map_t from_str_(reverse_map(to_string_map()));
        return from_str_;
    }
private:
    E Val_;

    static fromstr_map_t reverse_map(const tostr_map_t& eToS) {
        fromstr_map_t sToE;
        for (auto pr : eToS) {
            sToE.emplace(pr.second, pr.first);
        }
        return sToE;
    }
};

template <typename E>
streamable_enum<E> stream_enum(E e) {
    return streamable_enum<E>(e);
}

template <typename E>
typename streamable_enum<E>::tostr_map_t get_enum_strings() {
    // \todo throw an appropriate exception or display compile error/warning
    return {};
}

template <typename E>
std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
    auto& mp = streamable_enum<E>::to_string_map();
    auto res = mp.find(e);
    if (res != mp.end()) {
        os << res->second;
    } else {
        os.setstate(std::ios_base::failbit);
    }
    return os;
}

template <typename E>
std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
    std::string str;
    is >> str;
    if (str.empty()) {
        is.setstate(std::ios_base::failbit);
    }
    auto& mp = streamable_enum<E>::from_string_map();
    auto res = mp.find(str);
    if (res != mp.end()) {
        e = res->second;
    } else {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

#endif

उपयोग:

#include "streamable_enum.hpp"

using std::cout;
using std::cin;
using std::endl;

enum Animal {
    CAT,
    DOG,
    TIGER,
    RABBIT
};

template <>
streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
    return {
        { CAT, "Cat"},
        { DOG, "Dog" },
        { TIGER, "Tiger" },
        { RABBIT, "Rabbit" }
    };
}

int main(int argc, char* argv []) {
    cout << "What animal do you want to buy? Our offering:" << endl;
    for (auto pr : streamable_enum<Animal>::to_string_map()) {          // Use from_string_map() and pr.first instead
        cout << " " << pr.second << endl;                               // to have them sorted in alphabetical order
    }
    streamable_enum<Animal> anim;
    cin >> anim;
    if (!cin) {
        cout << "We don't have such animal here." << endl;
    } else if (anim == Animal::TIGER) {
        cout << stream_enum(Animal::TIGER) << " was a joke..." << endl;
    } else {
        cout << "Here you are!" << endl;
    }

    return 0;
}

0

यहाँ निम्नलिखित विशेषताओं के साथ मैक्रोज़ का उपयोग कर एक समाधान है:

  1. केवल एनम के प्रत्येक मूल्य को एक बार लिखें, इसलिए बनाए रखने के लिए कोई दोहरी सूची नहीं है

  2. बाद में एक अलग फ़ाइल में एनम मान न रखें जो बाद में #included है, इसलिए मैं इसे जहां चाहे लिख सकता हूं

  3. एनम को स्वयं प्रतिस्थापित न करें, मैं अभी भी एनम प्रकार को परिभाषित करना चाहता हूं, लेकिन इसके अलावा मैं प्रत्येक एनुम नाम को संबंधित स्ट्रिंग (विरासत कोड को प्रभावित नहीं करने के लिए) में मैप करने में सक्षम होना चाहता हूं

  4. खोज तेज़ होनी चाहिए, इसलिए अधिमानतः कोई स्विच-केस नहीं है, उन विशाल एनमों के लिए

https://stackoverflow.com/a/20134475/1812866


0

मैंने सोचा कि ढांचों और कक्षाओं को अपनाने के लिए बूस्ट.फ्यूजन एक जैसा समाधान अच्छा होगा, उन्होंने यह भी कुछ बिंदु पर, एक संलयन अनुक्रम के रूप में एनम का उपयोग करने के लिए किया था।

तो मैंने सिर्फ कुछ छोटे मैक्रों को बनाया ताकि एनमों को प्रिंट करने के लिए कोड तैयार किया जा सके। यह सही नहीं है और इसमें Boost.Fusion जनरेट किए गए बॉयलरप्लेट कोड के साथ देखने के लिए कुछ भी नहीं है, लेकिन Boost Fusion macros की तरह इसका उपयोग किया जा सकता है। मैं वास्तव में बूस्ट.फ्यूजन द्वारा आवश्यक प्रकार उत्पन्न करना चाहता हूं। इस बुनियादी ढांचे को एकीकृत करने के लिए जो संरचना सदस्यों के नामों को प्रिंट करने की अनुमति देता है, लेकिन यह बाद में होगा, अभी के लिए यह सिर्फ मैक्रोज़ है:

#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP

#include <swissarmyknife/detail/config.hpp>

#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>


#define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C(                     \
    R, unused, ENUMERATION_ENTRY)                                               \
    case ENUMERATION_ENTRY:                                                     \
      return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY);                             \
    break;                                                                      

/**
 * \brief Adapts ENUM to reflectable types.
 *
 * \param ENUM_TYPE To be adapted
 * \param ENUMERATION_SEQ Sequence of enum states
 */
#define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ)                   \
    inline std::string to_string(const ENUM_TYPE& enum_value) {                 \
      switch (enum_value) {                                                     \
      BOOST_PP_SEQ_FOR_EACH(                                                    \
          SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C,                   \
          unused, ENUMERATION_SEQ)                                              \
        default:                                                                \
          return BOOST_PP_STRINGIZE(ENUM_TYPE);                                 \
      }                                                                         \
    }                                                                           \
                                                                                \
    inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
      os << to_string(value);                                                   \
      return os;                                                                \
    }

#endif

नीचे दिया गया पुराना उत्तर बहुत बुरा है, कृपया इसका उपयोग न करें। :)

पुराना उत्तर:

मैं एक ऐसा रास्ता खोज रहा हूँ जो इस समस्या को हल करता है बिना बहुत अधिक एनमों के घोषणा सिंटैक्स। मैं एक समाधान के लिए आया था जो कड़े एनुम घोषणा से एक स्ट्रिंग को पुनः प्राप्त करने के लिए प्रीप्रोसेसर का उपयोग करता है।

मैं इस तरह गैर-विरल दुश्मनी को परिभाषित करने में सक्षम हूं:

SMART_ENUM(State, 
    enum State {
        RUNNING,
        SLEEPING, 
        FAULT, 
        UNKNOWN
    })

और मैं उनके साथ विभिन्न तरीकों से बातचीत कर सकता हूं:

// With a stringstream
std::stringstream ss;
ss << State::FAULT;
std::string myEnumStr = ss.str();

//Directly to stdout
std::cout << State::FAULT << std::endl;

//to a string
std::string myStr = State::to_string(State::FAULT);

//from a string
State::State myEnumVal = State::from_string(State::FAULT);

निम्नलिखित परिभाषाओं के आधार पर:

#define SMART_ENUM(enumTypeArg, ...)                                                     \
namespace enumTypeArg {                                                                  \
    __VA_ARGS__;                                                                         \
    std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) {                 \
            os << swissarmyknife::enums::to_string(#__VA_ARGS__, val);                   \
            return os;                                                                   \
    }                                                                                    \
                                                                                     \
    std::string to_string(const enumTypeArg& val) {                                      \
            return swissarmyknife::enums::to_string(#__VA_ARGS__, val);                  \
    }                                                                                    \
                                                                                     \
    enumTypeArg from_string(const std::string &str) {                                    \
            return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str);   \
    }                                                                                    \
}                                                                                        \


namespace swissarmyknife { namespace enums {

    static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            if (enumVal == count) {
                std::string identifiersSubset = identifiers.substr(0, found);
                size_t beginId = identifiersSubset.find_last_of("{,");
                identifiersSubset = identifiersSubset.substr(beginId+1);
                boost::algorithm::trim(identifiersSubset);
                return identifiersSubset;
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("The enum declaration provided doesn't contains this state.");
    }                                                  

    template <typename EnumType>
    static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            std::string identifiersSubset = identifiers.substr(0, found);
            size_t beginId = identifiersSubset.find_last_of("{,");
            identifiersSubset = identifiersSubset.substr(beginId+1);
            boost::algorithm::trim(identifiersSubset);

            if (identifiersSubset == enumStr) {
                return static_cast<EnumType>(count);
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("No valid enum value for the provided string");
    }                      

}}

जब मुझे विरल Enum के लिए समर्थन की आवश्यकता होगी और जब मेरे पास अधिक समय होगा तो मैं to_string और from_string कार्यान्वयन को बढ़ावा देने के साथ सुधार करूँगा :: xpressive , लेकिन यह संकलित समय के कारण महत्वपूर्ण templating प्रदर्शन और निष्पादित निष्पादन के कारण होगा वास्तव में बड़ा होने की संभावना है। लेकिन इसका यह फायदा है कि यह बदसूरत मैनुअल स्ट्रिंग हेरफेर कोड की तुलना में अधिक पठनीय और मुख्य योग्य होगा। : डी

अन्यथा मैं हमेशा बढ़ावा देता था :: enums मान और स्ट्रिंग के बीच इस तरह के मैपिंग करने के लिए bimap, लेकिन इसे मैन्युअल रूप से बनाए रखना होगा।


0

क्योंकि मैं सभी सामान्य कारणों से मैक्रोज़ का उपयोग नहीं करना पसंद करता हूं, मैंने एक अधिक सीमित मैक्रो समाधान का उपयोग किया, जिसमें एनम घोषणा मैक्रो को मुक्त रखने का लाभ है। नुकसान में प्रत्येक एनम के लिए मैक्रो डिफिनेशन को कॉपी पेस्ट करना शामिल है, और एनम में मान जोड़ते समय मैक्रो इनवोकेशन को स्पष्ट रूप से जोड़ना है।

std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs)
{
#define HANDLE(x) case x: os << #x; break;
    switch (cs) {
    HANDLE(CaptureState::UNUSED)
    HANDLE(CaptureState::ACTIVE)
    HANDLE(CaptureState::CLOSED)
    }
    return os;
#undef HANDLE
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.