2011-04-03 23 views
17

से एक enum सूची बनाना आदेश में मेरी कोड छोटा और आसान बनाने के लिए बदलने के लिए मैं एक मैक्रो के साथएक स्ट्रिंग सूची और एक सी ++ मैक्रो

enum{ E_AAA, E_BBB, E_CCC }; 
static const char *strings{"AAA", "BBB", "CCC" }; 

की तरह कुछ बदलना चाहते हैं, INIT (एएए, बीबीबी की तरह, सीसीसी); लेकिन जब मैं परिवर्तनीय तर्कों के साथ मैक्रो करने का प्रयास करता हूं, और स्ट्रिंगफिकेशन मुझे एक त्रुटि मिलती है क्योंकि तर्क घोषित नहीं किए जाते हैं।

यह कैसे करें इस पर कोई विचार है?

+3

वास्तव में किस प्रकार की तरह अपने मैक्रो देखो? – Xeo

+0

Boost.Preprocessor पर एक नज़र डालें, यह बदसूरत है (सीपीपी की सीमाओं के कारण) लेकिन यह povides का अर्थ है मैक्रोज़ लिखने के लिए जो अनुक्रमों पर काम करते हैं। – Begemoth

+2

लगता है जैसे 'स्ट्रिंग्स' परिभाषा को आउटपुट करते समय उद्धरण भूल गए हैं। इसके अलावा, क्या आप एक भाषा चुन सकते हैं: सी या सी ++? –

उत्तर

4

ऐसा करने का एक तरीका X-Macros के साथ है, जो मूल रूप से एक मैक्रो को परिभाषित करने का एक तरीका है जिसका उपयोग सरल मैक्रो की तुलना में अधिक जटिल संरचनाओं को उत्पन्न करने के लिए किया जाता है। आप जो पूछ रहे हैं वह करने के Here is an example

#include <boost/preprocessor.hpp> 

#define DEFINE_ENUM_DECL_VAL(r, name, val) BOOST_PP_CAT(name, BOOST_PP_CAT(_, val)) 
#define DEFINE_ENUM_VAL_STR(r, name, val) BOOST_PP_STRINGIZE(val) 
#define DEFINE_ENUM(name, val_seq)             \ 
    enum name {                  \ 
    BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_DECL_VAL, name, val_seq)) \ 
    };                    \ 
    static const char* BOOST_PP_CAT(name, _strings) {        \ 
    BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_VAL_STR, name, val_seq)) \ 
    }; 

DEFINE_ENUM(E, (AAA)(BBB)(CCC)) 

(AAA)(BBB)(CCC) पेड़ तत्वों एएए, बीबीबी और CCC के Boost.Preprocessor अनुक्रम है:

+0

+1 मैं बस इसे लिख रहा था, लेकिन आपको पहले से ही एक मौजूदा उदाहरण मिला :) – digEmAll

+0

एक्स-मैक्रोज़ के लिए खराब उदाहरण, क्योंकि प्रदाता पहले ही तर्कों को स्ट्रिंग कर सकता है; जिसका मतलब है कि पूरे एक्स (लाल, "लाल") को एक्स (लाल) के साथ एक बेहतर एक्स – UKMonkey

2

यहाँ Boost.Preprocessor साथ एक समाधान है।

2

इसे संभालने का एक तरीका सूची मैक्रो, यानी कुछ ऐसा है जो उपयोगकर्ता को परिभाषित करने के लिए छोड़ा गया है जो किसी अन्य मैक्रो में फैला हुआ है। उदाहरण के लिए:

#define MY_ENTRY(x) E_##x, 
enum name 
{ 
    MY_LIST 
    NUMBER_OF_ELEMENTS /* Needed to eat trailing comma (not needed in C99, but in C++) */ 
}; 
#undef MY_ENTRY 

स्ट्रिंग को परिभाषित करने के लिए::

#define MY_ENTRY(x) #x, 
static const char *strings[] = { MY_LIST }; 
#undef MY_ENTRY 

व्यक्तिगत रूप से, मैं इस बहुत आसान के साथ की तुलना में X मैक्रो काम करने के लिए, के रूप में पाते हैं

#define MY_LIST MY_ENTRY(AAA) MY_ENTRY(BBB) MY_ENTRY(CCC) 

enum परिभाषित करने के लिए यह शामिल फ़ाइल फ़ाइल में भरोसा नहीं करता है।

+0

के साथ प्रतिस्थापित किया जा सकता है। – Xeo

+0

मेरे पास मानकों की जांच थी, सी 99 में वे ठीक हैं (जो मुझे खबर थी - धन्यवाद)। हालांकि, वे न तो सी 8 9 और न ही सी ++ (1 99 8) द्वारा स्वीकार किए जाते हैं। – Lindydancer

+0

आप सही हैं, कॉमौ ऑनलाइन सी ++ 03 सख्त मोड में पिछली कॉमा को भी खारिज कर देता है। :) बस मेरी पिछली टिप्पणी को अनदेखा करें। – Xeo

22

यहां कुछ समाधान मैंने कुछ दिन पहले सीखा था। सरलीकृत संस्करण है कि आपके सवाल में आती है:

#define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)\ 
    enum name { v1, v2, v3, v4, v5, v6, v7};\ 
    const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7}; 

ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat); 

लेकिन अगर आप एक समारोह कॉल, इस तरह के साथ एक उन्नत संस्करण हो सकता है,:

#define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)\ 
    enum name { v1, v2, v3, v4, v5, v6, v7};\ 
    const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};\ 
    const char *name##ToString(value) { return name##Strings[value]; } 

ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat); 

यह बड़ा हो जाएगा होने के लिए:

enum Week { Sun, Mon, Tue, Wed, Thu, Fri, Sat}; 
    const char *WeekStrings[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 
    const char *WeekToString(value) { return WeekStrings[value]; }; 

आप ऑफसेट का उपयोग भी कर सकते हैं पहले तत्व के लिए, इस तरह:

#define ENUM_MACRO(name, offset, v1, v2, v3, v4, v5, v6, v7)\ 
    enum name { v1 = offset, v2, v3, v4, v5, v6, v7};\ 
    const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};\ 
    const char *name##ToString(value) { return name##Strings[value - offset ]; } 

ENUM_MACRO(Week, 1, Sun, Mon, Tue, Wed, Thu, Fri, Sat); 

मुझे उम्मीद है कि इससे मदद मिलती है।

लें देखभाल, Beco

संदर्भ:

Print the month question, कुश, एक सरल समाधान के लिए डैनी Varod

+0

कृपया, मॉडरेटर, सीडब्ल्यू ध्वज हटा दें। धन्यवाद। –

+0

जैसा कि हम देख सकते हैं कि ओपी साइट पर सक्रिय नहीं है। क्या यह संभव है (और वांछनीय!) एक मॉडरेटर को मेरे उत्तर को "स्वीकृत उत्तर" के रूप में स्वीकार करते हैं (यदि कोई सोचता है कि इसके लिए इसका मूल्य है)? प्रक्रिया पर किसी भी टिप्पणी के लिए धन्यवाद। –

+0

किसी के लिए यह संभव नहीं है लेकिन प्रश्न लेखक उत्तर स्वीकार करने के लिए। इस तरह के मामलों के लिए [स्वचालित डिफ़ॉल्ट के बारे में चर्चाएं] (http://meta.stackexchange.com/questions/8692/force-accepted-answers-on-questions-by-inactive-users) हैं, लेकिन 'स्वीकृत' था कभी भी 'सर्वश्रेष्ठ' को इंगित करने का इरादा नहीं है, केवल वह जवाब जो प्रश्न लेखक सबसे अधिक संबंधित हो सकता है। केवल प्रश्न लेखक ही जानता है कि निश्चित रूप से, इसलिए हम उस तरह के परिवर्तन को पेश करने में बहुत अनिच्छुक रहे हैं। –

1

द्वारा जवाब से, मैं X-Macros की तरह कुछ सलाह देते हैं।

एक और जटिल समाधान के लिए जो कई अन्य विशेषताओं (जैसे रेंज जांच, बढ़ी हुई प्रकार की सुरक्षा, वैकल्पिक संबंधित डेटा इत्यादि) जोड़ता है, वहां एक प्रस्तावित (लेकिन कभी अंतिम रूप दिया नहीं गया) Boost.Enum library है।

11

आप मैक्रो जादू का एक सा के साथ यह कर सकते हैं:

#include <iostream> 
#include <exception> 
#include <vector> 

#define FRUITS \ 
    etype(Unknown), \ 
    etype(Apple), \ 
    etype(Orange), \ 
    etype(Banana), \ 
    etype(Apricot), \ 
    etype(Mango) 

#define etype(x) F_##x 

typedef enum { FRUITS } Fruit; 

#undef etype 
#define etype(x) #x 

static const char *strFruit[] = { FRUITS }; 

const char *enum2str (Fruit f) 
{ 
    return strFruit[static_cast<int>(f)]; 
} 

Fruit str2enum (const char *f) 
{ 
    const int n = sizeof(strFruit)/sizeof(strFruit[0]); 
    for (int i = 0; i < n; ++i) 
    { 
     if (strcmp(strFruit[i], f) == 0) 
      return (Fruit) i; 
    } 
    return F_Unknown; 
} 

int main (int argc, char *argv[]) 
{ 
    std::cout << "I like " << enum2str(F_Mango) << std::endl; 
    std::cout << "I do not like " << enum2str(F_Banana) << std::endl; 
    std::vector<char *> v; 
    v.push_back("Apple"); 
    v.push_back("Mango"); 
    v.push_back("Tomato"); 
    for (int i = 0; i < v.size(); ++i) 
    { 
     const Fruit f = str2enum(v[i]); 
     if (f == F_Unknown) 
      std::cout << "Is " << v[i] << " a fruit?" << std::endl; 
     else 
      std::cout << v[i] << " is a fruit" << std::endl; 
    } 
    return 0; 
} 

यह आउटपुट:

I like Mango 
I do not like Banana 
Apple is a fruit 
Mango is a fruit 
Is Tomato a fruit? 
+1

मुझे यह पसंद है, लेकिन मुझे यहां एक समान लेकिन क्लीनर तरीका मिला: http://stackoverflow.com/a/238157/599142 – tr3w

7

यहाँ मेरी हल है

#define FRUITS \ 
    etype(Unknown), \ 
    etype(Apple), \ 
    etype(Orange), \ 
    etype(Banana), \ 
    etype(Apricot), \ 
    etype(Mango) 

#define etype(x) F_##x 

typedef enum { FRUITS } Fruit; 

#undef etype 
#define etype(x) #x 

static const char *strFruit[] = { FRUITS }; 

यहाँ एक परीक्षण कार्यक्रम है:

#define FRUITS(fruit) \ 
    fruit(Apple)  \ 
    fruit(Orange)  \ 
    fruit(Banana)  

#define CREATE_ENUM(name) \ 
    F_##name, 

#define CREATE_STRINGS(name) \ 
    #name, 

चाल यह है कि 'फल' मैक्रो 'फल' का तर्क है और जिसे आप पास करते हैं उसे बदल दिया जाएगा। उदाहरण के लिए:

FRUITS(CREATE_ENUM) 

यह करने के लिए विस्तार होगा:

F_Apple, F_Orange, F_Banana, 

enum और स्ट्रिंग सरणी बनाने देता है:

enum fruit { 
    FRUITS(CREATE_ENUM) 
}; 

const char* fruit_names[] = { 
    FRUITS(CREATE_STRINGS) 
}; 
+0

यह सुंदर है! मुझे वास्तव में इसकी आवश्यकता थी लेकिन कभी सोचा नहीं कि किसी ने इसे हल किया होगा। –

0

मैं पार्टी के लिए थोड़ी देर हो चुकी हूँ, लेकिन यहाँ है एक और सुझाव
यह MyEnumName और एक साथी स्थिर सहायक वर्ग Enumator<MyEnumName> कहता है, यह दृढ़ता से टाइप की गई एनम कक्षा बनाता है।
यह पिछले उत्तरों की तुलना में बड़ा है क्योंकि इसमें अधिक सुविधाएं हैं, उदा। स्ट्रीम ऑपरेटरों से/स्ट्रिंग में रूपांतरण के लिए।
ध्यान दें कि यह सूचकांक अनुक्रम के उपयोग के कारण सी ++ 14 मानक पर निर्भर करता है।

उपयोग:

/* One line definition - no redundant info */ 
ENUM_DEFINE(WeekDay /*first item is enum name*/, 
    Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday); 

/* works seemlessly with streams (good for logging) */ 
auto dayOne = WeekDay::Sunday; 
std::cout << "day of week is: " << day_of_week; 

/* explicit construction from string using WeekDay_enum companion class*/ 
auto dayTwo = Enumator<WeekDay>::fromString("Tuesday"); 


/*Iterate over all enum values using Enumator<WeekDay> companion class*/ 
std::cout << "Days of the week are:\n" 
for (auto enumVal : Enumator<WeekDay>::getValues()) { 
    std::cout << enumVal << "\n"; 
} 

स्रोत:

#include <array> 
    #include <string> 
    #include <sstream> 
    #include <stdexcept> 

template<typename E> 
using isEnum = typename std::enable_if<std::is_enum<E>::value>::type; 

template<typename E, typename = isEnum<E>> 
constexpr static int enumSize() { 
    return 0; 
} 

template<typename E, typename = isEnum<E>> 
inline static std::string getEnumStringValues() { 
    return ""; 
} 


/*Enum companion class to hold the methods that can't be declared in an enum*/ 
template<typename EnumType, isEnum<EnumType>* = nullptr> 
class Enumator 
{ 
    Enumator() = delete; /* prevents instantiation */ 

public: 

    constexpr static int size() { 
     return enumSize<EnumType>(); 
    } 
    /* list of all enum values a string */ 
    static auto const& getValuesStr() 
    { 
     static std::array<std::string, size()> values; 
     if (values[0].empty()) { 
      std::string valuesStr = getEnumStringValues<EnumType>(); 
      std::stringstream ss(valuesStr); 
      for (auto& value : values) { 
       std::getline(ss, value, ',');     
      } 
     } 
     return values; 
    }; 

    /* list of all enum values */ 
    static auto const& getValues() 
    { 
     static std::array<EnumType, size()> values{ make_array(std::make_index_sequence<size()>()) }; 
     return values; 
    }; 

    /* To/from string conversion */ 
    constexpr static std::string const& toString(EnumType arg) { 
     return getValuesStr()[static_cast<unsigned>(arg)]; 
    } 

    static EnumType fromString(std::string const& val) 
    { 
     /* Attempt at converting from string value */ 
     auto const& strValues = getValuesStr(); 

     for (unsigned int i = 0; i < strValues.size(); i++) 
     { 
      if (val == strValues[i]) 
      { 
       return static_cast<EnumType>(i); 
      } 
     } 
     throw std::runtime_error("No matching enum value found for token: " + val); 
    } 

private: 
    /* Helper method to initialize array of enum values */ 
    template<std::size_t...Idx> 
    static auto make_array(std::index_sequence<Idx...>) 
    { 
     return std::array<EnumType, size()>{{static_cast<EnumType>(Idx)...}}; 
    } 
}; 

template<typename EnumType, isEnum<EnumType>* = nullptr> 
inline std::istream& operator>> (std::istream& input, EnumType& arg) 
{ 
    std::string val; 
    input >> val; 
    arg = Enumator<EnumType>::fromString(val); 
    return input; 
} 

template<typename EnumType, isEnum<EnumType>* = nullptr> 
inline std::ostream& operator<< (std::ostream& output, const EnumType& arg) 
{ 
    return output << Enumator<EnumType>::toString(arg); 
} 

#define ENUM_DEFINE(EnumName,...)\ 
    \ 
    enum class EnumName;\ 
    \ 
    template<>\ 
    constexpr int enumSize<EnumName>() {\ 
     /*Trick to get the number of enum members:*/\ 
     /*dump all the enum values in an array and compute its size */\ 
     enum EnumName { __VA_ARGS__ }; \ 
     EnumName enumArray[]{ __VA_ARGS__ }; \ 
     return sizeof(enumArray)/sizeof(enumArray[0]); \ 
    }\ 
    \ 
    template<>\ 
    inline std::string getEnumStringValues<EnumName>() { return #__VA_ARGS__; }\ 
    \ 
    enum class EnumName : int { __VA_ARGS__ }