2013-04-16 4 views
7

यह प्रश्न "How to map strings to enums" का एन-वें पुनरावृत्ति हो सकता है।संकलित समय जांचें कि एनम मानचित्र पर स्ट्रिंग पूर्ण है

मेरी आवश्यकताएं थोड़ा आगे बढ़ती हैं और मैं throw एक निश्चित अपवाद चाहता हूं जब वैध इनपुट की सीमा में कोई कुंजी नहीं मिलती है। तो मैं इस EnumMap के इस कार्यान्वयन (जरूरतों const std::map परिभाषा के लिए बढ़ावा देने के) होते हैं:

#include <map> 
#include <string> 
#include <sstream> 
#include <stdexcept> 
#include <boost/assign.hpp> 

typedef enum colors { 
    RED, 
    GREEN, 
} colors; 
// boost::assign::map_list_of 
const std::map<std::string,int> colorsMap = boost::assign::map_list_of 
              ("red", RED) 
              ("green", GREEN); 
//----------------------------------------------------------------------------- 
// wrapper for a map std::string --> enum 
class EnumMap { 
private: 
    std::map<std::string,int> m_map; 
    // print the map to a string 
    std::string toString() const { 
    std::string ostr; 
    for(auto x : m_map) { 
     ostr += x.first + ", "; 
    } 
    return ostr; 
    } 
public: 
    // constructor 
    EnumMap(const std::map<std::string,int> &m) : m_map(m) { } 
    // access 
    int at(const std::string &str_type) { 
    try{ 
     return m_map.at(str_type); 
    } 
    catch(std::out_of_range) { 
     throw(str_type + " is not a valid input, try : " + toString()); 
    } 
    catch(...) { 
     throw("Unknown exception"); 
    } 
    } 
}; 
//----------------------------------------------------------------------------- 
int main() 
{ 
    EnumMap aColorMap(colorsMap); 
    try { 
    aColorMap.at("red"); // ok 
    aColorMap.at("yellow"); // exception : "yellow is not a valid input ..." 
    } 
    catch(std::string &ex) { 
    std::cout << ex << std::endl; 
    } 
    return 0; 
} 

यह अच्छी तरह से काम करता है और मैं क्या जरूरत है। अब, मैं संकलन समय पर जानना संभव बनाना चाहता हूं कि एक निश्चित enum में सभी तत्व EnumMap कन्स्ट्रक्टर को पास कर दिए गए हैं, और यह भी कि enum के सभी तत्व इसी स्ट्रिंग के साथ मेल खाते हैं।

मैं std::initializer_list और static_assert साथ कोशिश की, लेकिन ऐसा लगता है कि अभी भी VC2010 std::initializer_list का समर्थन नहीं करता (here देखें)।

क्या किसी को यह पता है कि इसे कार्यान्वित करना कैसे संभव होगा? शायद टेम्पलेट्स के साथ, या अपनी खुद की एनम कक्षा को लागू करना?

+1

आप अपने कोड शिपिंग से पहले, सही परीक्षण करते हैं? तो यह सुनिश्चित करने के लिए एक रनटाइम चेक पर्याप्त होगा, क्या आपको नहीं लगता? –

+0

एकमात्र भाषा निर्माण जो मुझे पता है, आपको चेतावनी देगा यदि आप एक मूल्य मान चूक गए हैं तो 'स्विच' है यदि आप प्रत्येक मान के लिए 'केस' जोड़ते हैं और आपके पास' डिफ़ॉल्ट 'शाखा नहीं है। मुझे लगता है कि इसका मतलब है कि आपको एक मैक्रो की आवश्यकता होगी लेकिन मैं कुछ विशिष्ट सुझाव देने से बहुत दूर हूं :) –

+0

मैं मानता हूं कि रन-टाइम परीक्षण पर्याप्त होना चाहिए, और मुझे चेतावनी के बारे में भी पता है। मुझे लगता है कि मैं यह समझने की कोशिश कर रहा हूं कि इसे – FKaria

उत्तर

2

क्या किसी को यह पता है कि इसे कार्यान्वित करना कैसे संभव होगा? शायद टेम्पलेट्स के साथ, या अपनी खुद की एनम कक्षा को लागू करना?

ऐसा करना संभव नहीं है। Std :: मानचित्र के साथ नहीं, और टेम्पलेट मेटा-प्रोग्रामिंग के साथ नहीं।

4
typedef enum colors { 
    MIN_COLOR, 
    RED = MIN_COLOR, 
    GREEN, 
    MAX_COLOR 
} colors; 

template< colors C > 
struct MapEntry { 
    std::string s; 
    MapEntry(std::string s_):s(s_) {} 
}; 
void do_in_order() {} 
template<typename F0, typename... Fs> 
void do_in_order(F0&& f0, Fs&&... fs) { 
    std::forward<F0>(f0)(); 
    do_in_order(std::forward<Fs>(fs)...); 
} 
struct MapInit { 
    std::map< std::string, color > retval; 
    operator std::map< std::string, color >() { 
    return std::move(retval); 
    } 
    template<colors C> 
    void AddToMap(MapEntry<C>&& ent) { 
    retval.insert(std::make_pair(std::move(end.s), C)); 
    } 
    template< typename... Entries > 
    MapInit(Entries&& entries) { 
    do_in_order([&](){ AddToMap(entries); }...); 
    } 
}; 
template<typename... Entries> 
MapInit mapInit(Entries&&... entries) { 
    return MapInit(std::forward<Entries>(entries)...); 
} 
const std::map<std::string, colors> = mapInit(MapEntry<RED>("red"), MapEntry<GREEN>("green")); 

जो आपको एक सी ++ 11 रास्ता संकलन समय color से एक std::map का निर्माण और रन-टाइम string डेटा देता है।

की सूची colors की सूची में "मेटाफंक्शन" की सूची में फेंको।

template<colors... Cs> 
struct color_list {}; 
template<typename... Ts> 
struct type_list {}; 
template<typename MapEnt> 
struct extract_color; 
template<colors C> 
struct extract_color<MapEntry<C>> { 
    enum {value=C}; 
}; 
template<typename Entries> 
struct extract_colors; 
template<typename... MapEntries> 
struct extract_colors<type_list<MapEntries...>> { 
    typedef color_list< ((colors)extract_colors<MapEntries>::value)... > type; 
}; 

उस सूची को सॉर्ट करें। डुप्लिकेट का पता लगाएं - यदि वहां हैं, तो आप खराब हो गए हैं।

संकलन-समय सॉर्टिंग बाकी के मुकाबले कठिन है, और कोड की 100+ रेखाएं हैं। यदि आप बहुत ज्यादा ध्यान नहीं देते हैं तो मैं इसे छोड़ दूंगा! Here is a compile time merge sort I wrote in the past to answer a stack overflow question जो अपेक्षाकृत सरल अनुकूलन के साथ काम करेगा (यह मूल्यों के साथ प्रकारों को प्रकार देता है, इस मामले में हम सीधे संकलन-समय मानों की एक सूची क्रमबद्ध कर रहे हैं)।

// takes a sorted list of type L<T...>, returns true if there are adjacent equal 
// elements: 
template<typename clist, typename=void> 
struct any_duplicates:std::false_type {}; 
template<typename T, template<T...>class L, T t0, T t1, T... ts> 
struct any_duplicates< L<t0, t1, ts...>, typename std::enable_if<t0==t1>::type>: 
    std::true_type {}; 
template<typename T, template<T...>class L, T t0, T t1, T... ts> 
struct any_duplicates< L<t0, t1, ts...>, typename std::enable_if<t0!=t1>::type>: 
    any_duplicates< L<t1, ts...> > {}; 

colors (यानी, <MIN_COLOR या >=MAX_COLOR) की मान्य श्रेणी से बाहर तत्वों का पता लगाने। यदि हां, तो आप खराब हो गए।

template<typename List> 
struct min_max; 
template<typename T, template<T...>class L, T t0> 
struct min_max { 
    enum { 
    min = t0, 
    max = t1, 
    }; 
}; 
template<typename T, template<T...>class L, T t0, T t1, T... ts> 
struct min_max { 
    typedef min_max<L<t1, ts...>> rest_of_list; 
    enum { 
    rest_min = rest_of_list::min, 
    rest_max = rest_of_list::max, 
    min = (rest_min < t0):rest_min:t0, 
    max = (rest_max > t0):rest_max:t0, 
    }; 
}; 
template< typename T, T min, T max, typename List > 
struct bounded: std::integral_constant< bool, 
    (min_max<List>::min >= min) && (min_max<List>::max < max) 
> {}; 

गणना देखते हैं कि कितने तत्वों - वहाँ MAX_COLOR तत्वों होना चाहिए। यदि नहीं, तो आप खराब हो गए।

template<typename List> 
struct element_count; 
template<typename T, template<T...>L, T... ts> 
struct element_count<L<ts...>>:std::integral_constant< std::size_t, sizeof...(ts) > {}; 

यदि इनमें से कोई भी नहीं हुआ, तो कबूतर द्वारा आपको उनमें से प्रत्येक को प्रारंभ करना होगा।

एकमात्र चीज गायब है कि आप चले गए थे और दो मानों के लिए उसी string का उपयोग कर सकते थे।संकलन समय string एस दर्द के रूप में, बस इसे रन टाइम पर जांचें (map में प्रविष्टियों की संख्या colors की संख्या को प्रारंभ करने के बाद बराबर होती है)।

सी ++ 03 में ऐसा करना कठिन होगा। आपको विविध टेम्पलेट्स की कमी है, इसलिए आप उन्हें नकली बनाते हैं। दर्द क्या है। mpl आपकी मदद करने में सक्षम हो सकता है।

वैरार्डिक टेम्पलेट्स नवंबर 2012 एमएसवीसी सीटीपी कंपाइलर अपडेट में उपलब्ध हैं।

डुप्लिकेट जांच के बिना और बाउंड जांच के बिना एक खिलौना उदाहरण है (यह सिर्फ जांचता है कि मानचित्र प्रविष्टियों के मिलान की संख्या);

#include <cstddef> 
#include <utility> 
#include <string> 
#include <map> 

enum TestEnum { 
    BeginVal = 0, 
    One = BeginVal, 
    Two, 
    Three, 
    EndVal 
}; 

template<TestEnum e> 
struct MapEntry { 
    enum { val = e }; 
    std::string s; 
    MapEntry(std::string s_):s(s_) {} 
}; 

void do_in_order() {} 
template<typename F0, typename... Fs> 
void do_in_order(F0&& f0, Fs&&... fs) { 
    std::forward<F0>(f0)(); 
    do_in_order(std::forward<Fs>(fs)...); 
} 

template<typename... MapEntries> 
struct count_entries:std::integral_constant< std::size_t, sizeof...(MapEntries) > {}; 

// should also detect duplicates and check the range of the values: 
template<typename... MapEntries> 
struct caught_them_all: 
    std::integral_constant< 
    bool, 
    count_entries<MapEntries...>::value == (TestEnum::EndVal-TestEnum::BeginVal) 
    > 
{}; 

struct BuildMap { 
    typedef std::map<std::string, TestEnum> result_map; 
    mutable result_map val; 
    operator result_map() const { 
    return std::move(val); 
    } 
    template<typename... MapEntries> 
    BuildMap(MapEntries&&... entries) { 
    static_assert(caught_them_all<MapEntries...>::value, "Missing enum value"); 
    bool _[] = { ((val[ entries.s ] = TestEnum(MapEntries::val)), false)... }; 
    } 
}; 

std::map< std::string, TestEnum > bob = BuildMap(
    MapEntry<One>("One") 
    ,MapEntry<Two>("Two") 
#if 0 
    ,MapEntry<Three>("Three") 
#endif 
); 

int main() {} 

#if 1 साथ #if 0 बदलें यह संकलन को देखने के लिए। Live link अगर आप खेलना चाहते हैं।

+0

आपने यह नहीं दिखाया कि संकलन त्रुटि –

+0

'static_assert (खंड," चीजें अच्छी नहीं हैं ") को संकलन त्रुटि प्राप्त करने के लिए C++ 11 तरीका है। या क्या आप संकलन-समय सॉर्टिंग और विशिष्टता परीक्षण, सीमाओं की जांच, और समान गुण कक्षाओं का मतलब था? – Yakk

+0

ग्रेट उत्तर। ऐसा लगता है कि यह केवल एक विशिष्ट 'enum' प्रकार, अर्थात् 'रंग' के लिए काम करेगा। ऐसा लगता है कि यह संभव है कि मैं जो करने की कोशिश कर रहा हूं उसे प्राप्त करने के लिए जितना संभव हो सके, और बहुत आसान समाधान संभव है क्योंकि आप बहुत भारी वैपनों का उपयोग कर रहे हैं। मैं सोच रहा हूं कि अद्वितीय अन्य विकल्प कस्टम तत्वों को लागू करने के लिए कस्टम 'एनम' वर्ग और कस्टम 'एनममैप' को लागू करना होगा। – FKaria

0

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

// 
// Cumstom Enum Header File 
// 

#include <vector> 
#include <string> 

#include <boost/algorithm/string/classification.hpp> 
#include <boost/algorithm/string/split.hpp> 
#include <boost/range/algorithm/find.hpp> 

std::vector<std::string> split_to_vector(std::string s) 
{ 
    // splits a comma separated string to a vector of strings 
    std::vector<std::string> v; 
    boost::split(v, s, boost::is_any_of(", "), boost::token_compress_on); 
    return v; 
} 

#define ENUM_TO_STRING(X,...) \ 
    struct X { \ 
     enum Enum {__VA_ARGS__}; \ 
     static \ 
     std::string to_string(X::Enum k) { \ 
      std::vector<std::string> keys = split_to_vector(#__VA_ARGS__); \ 
      return keys[static_cast<int>(k)]; \ 
     } \ 
     static \ 
     X::Enum which_one(const std::string s) { \ 
      std::vector<std::string> keys = split_to_vector(#__VA_ARGS__); \ 
      auto it = boost::find(keys, s); \ 
      if(it == keys.end()) { \ 
       throw("not a valid key"); \ 
      } \ 
      return static_cast<X::Enum>(it - keys.begin()); \ 
     } \ 
    } 


// 
// Usage 
// 

#include <iostream> 

ENUM_TO_STRING(Color, Red, Green, Blue); 

int main() 
{ 
    std::string red_s = Color::to_string(Color::Red); 
    std::cout << red_s << std::endl; 

    Color::Enum red_e = Color::which_one("Red"); 
    std::cout << red_e << std::endl; 

    // won't compile 
    // std::string yellow_s = Colors::to_string(Colors::Yellow); 

    // run-time error 
    // Color::Enum yellow_e = Colors::which_one("Yellow"); 
} 

Coliru पर यह रन: http://coliru.stacked-crooked.com/a/e81e1af0145df99a

+0

आप इसे संकलित समय बना सकते हैं। [यहां] देखें (http://stackoverflow.com/questions/28828957/enum-to-string-in-modern-c-and-future-c17/31362042#31362042)। संकलन-समय 'to_string' के साथ-साथ उस समाधान का एक संस्करण भी है। – antron

+0

धन्यवाद @antron। मुझे बूस्ट प्रीप्रोसेसर लाइब्रेरी का उपयोग करके संकलन समय पर वेक्टर को परिभाषित करने का एक तरीका भी मिला, लेकिन मैंने अभी इस जवाब को अपडेट नहीं किया है। मैंने अभी [समाधान] [http://stackoverflow.com/a/35788543/1405424) में अपना समाधान पोस्ट किया है। मुझे विश्वास है कि आपके समाधान के समान है, सिवाय इसके कि यह मैक्रोज़ के लिए बूस्ट प्रीप्रोसेसर का उपयोग करता है। मैंने स्ट्रिंग -> enum रूपांतरण भी छोड़ा क्योंकि सवाल केवल enum -> स्ट्रिंग के लिए पूछता है। – FKaria

संबंधित मुद्दे