कैसे आसानी से स्ट्रिंग के लिए ग + + एनम को मैप करें


119

मेरे पास कुछ लाइब्रेरी हेडर फ़ाइलों में एनम प्रकारों का एक गुच्छा है जो मैं उपयोग कर रहा हूं, और मैं उपयोगकर्ता स्ट्रिंग्स - और इसके विपरीत एनुम मानों को परिवर्तित करने का एक तरीका चाहता हूं।

RTTI मेरे लिए ऐसा नहीं करेगा, क्योंकि 'उपयोगकर्ता तार' को एनुमरेशन्स की तुलना में थोड़ा अधिक पठनीय होना चाहिए।

एक जानवर बल समाधान इस तरह के कार्यों का एक गुच्छा होगा, लेकिन मुझे लगता है कि थोड़ा सा भी सी-लाइक है।

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

मुझे लगता है कि टेम्पलेट्स का उपयोग कर एक सुंदर समाधान है, लेकिन मुझे यह महसूस नहीं हो रहा है कि मैं अभी तक अपना सिर गोल नहीं कर सकी हूं।

अद्यतन: सुझावों के लिए धन्यवाद - मुझे यह स्पष्ट करना चाहिए था कि एनमों को एक तृतीय-पक्ष लाइब्रेरी हेडर में परिभाषित किया गया है, इसलिए मैं उनकी परिभाषा नहीं बदलना चाहता।

मेरा मन कर रहा है कि अब टेंपरेचर से बचें और ऐसा कुछ करें:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);

जवाबों:


60

अगर आप चाहते हैं कि एनम खुद को स्ट्रिंग्स के रूप में नाम दें, तो इस पोस्ट को देखें । अन्यथा, एक std::map<MyEnum, char const*>अच्छी तरह से काम करेगा। (अपने स्ट्रिंग शाब्दिकों को std :: मैप में स्ट्रिंग्स कॉपी करने का कोई मतलब नहीं)

अतिरिक्त संश्लिष्ट चीनी के लिए, यहाँ एक map_init वर्ग कैसे लिखा जाए। लक्ष्य की अनुमति है

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

फ़ंक्शन template <typename T> map_init(T&)वापस आता है map_init_helper<T>map_init_helper<T>एक टी एंड स्टोर करता है, और तुच्छ को परिभाषित करता है map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&)। (रिटर्निंग *thisसे operator()की श्रंखला की अनुमति देता है operator(), जैसे operator<<पर std::ostreamरों)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

चूंकि फंक्शन और हेल्पर क्लास टेम्पर्ड हैं, आप उन्हें किसी भी मैप, या मैप जैसी संरचना के लिए उपयोग कर सकते हैं। यानी इसमें प्रविष्टियों को भी जोड़ा जा सकता हैstd::unordered_map

यदि आप इन सहायकों को लिखना पसंद नहीं करते हैं, तो बढ़ावा दें: असाइन करें बॉक्स के बाहर समान कार्यक्षमता प्रदान करता है।


आपको दूसरे प्रश्न का संदर्भ देना सही है। लोगों को पोस्ट करने से पहले "संबंधित प्रश्नों" पर एक नज़र
डालनी चाहिए

2
@xtofl: यहां दिखाए गए "संबंधित प्रश्न" सूचीबद्ध किए गए संबंधित प्रश्नों से बिल्कुल अलग हैं जब मैंने प्रश्न पोस्ट किया था!
रॉडी

@MSalters, एक std :: नक्शा कार्यान्वयन से निपटने का एक उपयोगी तरीका है, लेकिन मैं बॉयलरप्लेट कोड को कम करने के कुछ तरीकों की तलाश कर रहा हूं जिनकी आवश्यकता हो सकती है।
रॉडी

@MSalters, ऑपरेटर [] के लिए कई तर्क स्वीकार करने में सक्षम होना अच्छा होगा। लेकिन दुख की बात है कि कोई ऐसा नहीं कर सकता। x [a, b] का मूल्यांकन x [b] करें। , (ए, बी) अभिव्यक्ति अल्पविराम ऑपरेटर का उपयोग करता है। इसलिए यह आपके कोड में ["A"] ["B"] ["C"] के बराबर है। आप इसे कहने के लिए इसे बदल सकते हैं [eValue1] ["A"] [eValu ..
-

फ़ंक्शन कॉल ऑपरेटर एक अच्छा उम्मीदवार भी होगा: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... तो यह बढ़ावा देने के बराबर है: असाइन करें: सम्मिलित करें (MyMap) (eValue1,) "ए") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
-

31

MSalters समाधान एक अच्छा है, लेकिन मूल रूप से फिर से लागू होता है boost::assign::map_list_of। यदि आपके पास बढ़ावा है, तो आप इसे सीधे उपयोग कर सकते हैं:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}

आप इसका उपयोग कैसे करेंगे जहां eeeToString एक वर्ग का डेटा सदस्य है? मुझे "त्रुटि: डेटा सदस्य आरंभीकरण की अनुमति नहीं है"
प्रयोक्ता

@ उपयोगकर्ता: क्लास डेटा के सदस्यों को कंस्ट्रक्टरों में आरंभिक रूप से आरंभिक सूची में रखा जाता है।
MSalters

क्या यह काम सभी एनमों के लिए करना है। मेरे पास कई एनुम घोषणाएं हैं और वे नहीं चाहते कि मानचित्र केवल eeeआपके मामले में टाइप के लिए काम करे ।
जस्टिन लियांग

मैं एक टेम्पलेट का उपयोग करने की कोशिश की, लेकिन फिर मिल गया और त्रुटि error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString':।
जस्टिन लियांग

4
वास्तव में यह उत्तर मुख्य रूप से C ++ 11 के साथ अप्रचलित है।
अलास्टेयर

19

ऑटो-जनरेट एक फॉर्म दूसरे से।

स्रोत:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

जनरेट की गई

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

यदि Enum मान बड़े हैं, तो एक उत्पन्न प्रपत्र का उपयोग कर सकते हैं unordered_map <> या टेम्पलेट्स जैसा कि कॉन्स्टेंटिन द्वारा सुझाया गया है।

स्रोत:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

जनरेट की गई

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

उदाहरण:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}

सबसे तेज़ होते हुए भी यह @MSalters जितना आसान नहीं है।
केनी डेके

2
यदि आपके पास पाठ फ़ाइल से तार की एक सूची को पढ़ने और संकलन समय पर स्थैतिक चार्ट के साथ .h फ़ाइल उत्पन्न करने के लिए थोड़ा-सा पर्ल / पाइथन है। = "कार्यक्रम लिखने के लिए कार्यक्रम लिखें"
मार्टिन बेकेट

@ mgb: perl / python एकमात्र विकल्प नहीं हैं, जो किसी भी भाषा में लगभग कोई भी टेम्पलेट इंजन करेगा (इस मामले में एक टेम्पलेट से दोनों रूपों को उत्पन्न कर रहा है)।
JFS

@jf। हां, महत्वपूर्ण बिंदु स्वचालित रूप से संकलन समय पर स्थैतिक डेटा तालिकाओं का निर्माण करना था। मैं शायद सिर्फ एक बेवकूफ स्थिर सरणी पैदा करना पसंद करूंगा।
मार्टिन बेकेट

यदि संकलित समय पर राज्य का पता नहीं है तो क्या यह काम करेगा? मुझे पूरा यकीन है कि यह नहीं होगा - सिद्धांत रूप में कंपाइलर को एनम के सभी संभावित मूल्यों के साथ enum2str टेम्पलेट को इंस्टेंट करना होगा, जो कि मुझे पूरा यकीन है कि जीसीसी (कम से कम) ऐसा नहीं करेगा।
एलिस्टेयर

11

मुझे याद है कि StackOverflow पर कहीं और इसका उत्तर दिया गया है। यहां दोहरा रहा हूं। मूल रूप से यह वैरिएड मैक्रोज़ पर आधारित एक समाधान है, और इसका उपयोग करना बहुत आसान है:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

अपने कोड में इसका उपयोग करने के लिए, बस करें:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;

1
बस पूर्व c ++ 11 पर काम करने के लिए एनम वर्ग घोषणा को बदलें।
देवदत्त बसु

1
आप सही हैं यह काम करता है (ऑटो भी केवल सी + 11 है)। अच्छा समाधान! यह सही होगा यदि आप कुछ
एनमों के

मुझे लगता है कि मैंने कुछ ऐसा ही देखा है
सर्जेई

10

मेरा सुझाव है कि एक्स-मैक्रोज़ का उपयोग करने का एक मिश्रण सबसे अच्छा समाधान और निम्नलिखित टेम्पलेट फ़ंक्शन हैं:

Marcinkoziukmyopenidcom से उधार लेने और विस्तारित करने के लिए

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

क्या एनुम स्ट्रिंग सरणी परिभाषा को सामान्य बनाने का एक तरीका है? (मुझे नहीं पता कि मैक्रो के अंदर एक एक्स-मैक्रो को कैसे संभालना है और मैं टेम्पलेट को आसानी से नहीं संभालता हूं)
जोनाथन

5

मैं इस समाधान का उपयोग करता हूं जिसे मैं नीचे पुन: प्रस्तुत करता हूं:

#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;
}

1
यह मूल एक्स मैक्रोज़ है, और मैं चौंका हूं कि यह सुझाव देने के लिए यहां पहला उत्तर है! +1
हल्की दौड़ ऑर्बिट में

4

यदि आप MyEnum चर का स्ट्रिंग प्रतिनिधित्व प्राप्त करना चाहते हैं , तो टेम्पलेट इसे काट नहीं पाएंगे। टेम्पलेट को संकलन-समय पर ज्ञात अभिन्न मूल्यों पर विशेष किया जा सकता है।

हालाँकि, यदि आप यही चाहते हैं तो कोशिश करें:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

यह क्रिया है, लेकिन त्रुटियों को पकड़ लेगा, जैसे आपने प्रश्न किया है - आपका case VAL1डुप्लिकेट है।


वास्तव में विधि का नाम () आवश्यक नहीं है। मेरा जवाब देखिए।
JFS

3

मैंने इस विषय पर शोध करने में अधिक समय बिताया है जिसे मैं स्वीकार करना चाहता हूं। सौभाग्य से वहाँ जंगली में महान खुला स्रोत समाधान कर रहे हैं।

ये दो महान दृष्टिकोण हैं, भले ही पर्याप्त रूप से (अभी तक) ज्ञात न हों,

wise_enum

  • C ++ 11/14/17 के लिए स्टैंडअलोन स्मार्ट एनम लाइब्रेरी। यह सभी मानक कार्यक्षमता का समर्थन करता है जो आप C ++ में एक स्मार्ट एनम वर्ग से अपेक्षा करेंगे।
  • सीमाएं: कम से कम C ++ 11 की आवश्यकता होती है।

बेहतर एनम

  • एक ही हेडर फ़ाइल में और निर्भरता के बिना स्वच्छ वाक्यविन्यास के साथ चिंतनशील संकलन-समय एनुम लाइब्रेरी।
  • सीमाएं: मैक्रोज़ पर आधारित, एक वर्ग के अंदर इस्तेमाल नहीं की जा सकती।

2

मैं एक नक्शा मीटर है - और यह एनम में एम्बेड करने के लिए परीक्षा होगी।

m [MyEnum.VAL1] = "मान 1" के साथ सेटअप;

और सब हो गया


2

दूसरों से डिबगिंग / विश्लेषण कोड के लिए मुझे कई बार इस कार्यक्षमता की आवश्यकता है। इसके लिए, मैंने एक पर्ल स्क्रिप्ट लिखी है जो कई अतिभारित toStringतरीकों के साथ एक वर्ग उत्पन्न करती है । प्रत्येक toStringविधि Enumएक तर्क के रूप में लेती है और लौटती है const char*

बेशक, स्क्रिप्ट सी + + को केवल एनम के लिए पार्स नहीं करती है, लेकिन प्रतीक तालिका बनाने के लिए ctags का उपयोग करती है।

पर्ल स्क्रिप्ट यहाँ है: http://heinitz-it.de/download/enum2string/enum2string.b.html


2

आपके जवाब ने मुझे कुछ मैक्रोज़ लिखने के लिए प्रेरित किया। मेरी आवश्यकताएं निम्नलिखित थीं:

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

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

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

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

यह कोड कुछ मूल्यों के साथ एक क्लासिक एनम बनाता है। इसके अलावा यह std :: मैप के रूप में बनता है, जो प्रत्येक एनम वैल्यू को नाम देता है (यानी मैप [E_SUNDAY] = "E_SUNDAY", आदि)

ठीक है, यहाँ अब कोड है:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // यह वह फ़ाइल है जिसे आप जब भी इस सामान को करना चाहते हैं, शामिल करना चाहते हैं, तो आप मैक्रोज़ का उपयोग करेंगे:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // यह कस्टम एनम बनाने के लिए इसका उपयोग करने का एक उदाहरण है:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

चीयर्स।


2

यहाँ << और >> स्ट्रीम ऑपरेटर्स को एनम पर स्वचालित रूप से केवल एक लाइन मैक्रो कमांड के साथ प्राप्त करने का प्रयास है ...

परिभाषाएं:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

उपयोग:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

हालांकि इस योजना की सीमाओं के बारे में निश्चित नहीं है ... टिप्पणियों का स्वागत है!


1

हेडर में:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

इन .cpp फ़ाइल:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

कैविएट: खराब एरे इंडेक्स को न संभालें। :) लेकिन आप आसानी से सरणी से स्ट्रिंग प्राप्त करने से पहले एनम को सत्यापित करने के लिए एक फ़ंक्शन जोड़ सकते हैं।


वास्तव में एक बहुत ही गैर- DRY- स्पॉट समाधान।
xtofl

अब जब आप DRY का उल्लेख करते हैं। .h और .cpp फ़ाइल को कुछ अन्य इनपुट फ़ाइल से स्वचालित रूप से जनरेट किया जाता है। मैं बेहतर समाधान देखना पसंद करूंगा (कि अनावश्यक जटिलता का सहारा न लें)
moogs

1

मैं बस मैक्रोज़ का उपयोग करके इस संभव सुरुचिपूर्ण समाधान को दिखाना चाहता था। यह समस्या को हल नहीं करता है, लेकिन मुझे लगता है कि यह समस्या के बारे में पुनर्विचार करने का एक अच्छा तरीका है।

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- संपादित करें ----

कुछ इंटरनेट अनुसंधान और कुछ स्वयं के बाद मैं निम्नलिखित समाधान के लिए आया:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

मैं बस इसे पोस्ट करना चाहता था शायद कोई इस समाधान को उपयोगी पा सके। टेम्प्लेट कक्षाओं की कोई आवश्यकता नहीं है c ++ 11 की आवश्यकता नहीं है और इसे बढ़ावा देने की आवश्यकता नहीं है, इसलिए इसे सरल सी के लिए भी इस्तेमाल किया जा सकता है।

---- EDIT2 ----

2 से अधिक enums (संकलक समस्या) का उपयोग करते समय सूचना तालिका कुछ समस्याओं का उत्पादन कर सकती है। निम्नलिखित वर्कअराउंड ने काम किया:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

ऊपर मेरा सरल उपाय है। इसका एक लाभ 'एनयूएम' है जो संदेश सरणी के आकार को नियंत्रित करता है, यह सीमा से बाहर जाने से रोकता है (यदि आप इसे बुद्धिमानी से उपयोग करते हैं)।

आप स्ट्रिंग प्राप्त करने के लिए एक फ़ंक्शन को भी परिभाषित कर सकते हैं:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

इसके बाद मेरे समाधान के लिए, मैंने फिर निम्नलिखित को काफी दिलचस्प पाया। यह आम तौर पर उपरोक्त एक की सिंक समस्या को हल करता है।

यहां स्लाइड करें: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

यहाँ कोड: https://github.com/arunksaha/enum_to_string


1

मुझे पता है कि मुझे पार्टी करने में देर हो रही है, लेकिन बाकी सभी के लिए, जो इस पृष्ठ पर आने के लिए आते हैं, आप यह कोशिश कर सकते हैं, यह वहां की हर चीज से ज्यादा आसान है और इसे और अधिक बनाता है:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

क्या आप स्ट्रिंग्स का उपयोग करने और एनम का उपयोग नहीं करने का सुझाव दे रहे हैं? यह वास्तव में समस्या का समाधान नहीं है।
रॉडी

0

मैं हाल ही में एक विक्रेता पुस्तकालय (Fincad) के साथ एक ही मुद्दा था। सौभाग्य से, विक्रेता ने सभी enums के लिए xml doucumentation प्रदान किया। मैंने प्रत्येक enum प्रकार के लिए एक मानचित्र तैयार करना और प्रत्येक enum के लिए एक लुकअप फ़ंक्शन प्रदान करना समाप्त कर दिया। यह तकनीक आपको एनम की सीमा के बाहर एक लुकअप को बाधित करने की अनुमति देती है।

मुझे यकीन है कि swig आपके लिए कुछ ऐसा ही कर सकता है, लेकिन मुझे कोड जेनरेशन के बर्तन उपलब्ध कराने में खुशी हो रही है जो रूबी में लिखे गए हैं।

यहाँ कोड का एक नमूना है:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

लगता है कि आप दूसरे रास्ते पर जाना चाहते हैं (enum to string, बजाय string to enum), लेकिन यह रिवर्स करने के लिए तुच्छ होना चाहिए।

-Whit


1
a) क्या किसी और को यह बिल्कुल अपठनीय लगता है? कुछ टंकण और घोषणाओं के उपयोग से पठनीयता में काफी सुधार होता है। ख) स्थानीय स्थैतिक घोषणाएँ थ्रेडसेफ़ नहीं हैं। ग) कास्ट स्ट्रिंग और चार के बजाय * का उपयोग करें, घ) उस मूल्य के बारे में जो अपवाद में नहीं पाया जा सकता है?
एलास्टेयर

0

देखें कि निम्नलिखित सिंटैक्स आपको सूट करता है:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

यदि ऐसा होता है, तो आप इस लेख को देखना चाह सकते हैं:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


0

यह सही पुरानी गड़बड़ी एसओ से बिट्स और पेयर्स पर आधारित मेरा प्रयास है। For_each को 20 से अधिक enum मानों का समर्थन करने के लिए विस्तारित करना होगा। यह दृश्य स्टूडियो 2019, क्लैंग और जीसीसी पर परीक्षण किया गया। c ++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

जो निम्न कोड का उत्पादन करता है

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

इस तरह की शर्म की बात है कि आपको दुनिया में सबसे अधिक इस्तेमाल की जाने वाली प्रोग्रामिंग भाषाओं में से एक में ऐसा करने के लिए प्रीप्रोसेसर के साथ कूदना होगा ...


0

निर्दिष्ट एरेना इनिशियलाइज़र का उपयोग करके आपका स्ट्रिंग ऐरे एनम में तत्वों के क्रम से स्वतंत्र है:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.