Compilation time checks that the line for the enumeration map is complete

This question may well be the nth iteration of "How to match strings for enumerations . "

My requirements go a bit further, and I want to throw specific exception if the key is not found in valid inputs. Therefore, I have this implementation of this EnumMap (requires a raise to define 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; } 

It works well and does what I need. Now I want to know at compile time that all elements in a specific enum are passed to the EnumMap constructor, and also that all elements in enum mapped to the corresponding string.

I tried with std :: initializer_list and static_assert , but it looks like VC2010 still does not support std::initializer_list (see here ).

Does anyone have an idea on how this can be implemented? Perhaps with templates or with the implementation of my own Enum class?

+7
source share
3 answers
 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") ); 

which gives you a C ++ 11 way to build std::map from color compilation time and string runtime.

Add the following MapEntry<colors> to the colors list. "

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

Sort this list. Duplicate detection - if you have, you squinted.

Sorting sorting time is harder than the rest, and more than 100 lines of code. I will leave it if you do not mind too much! Here is a compilation of merge sort time that I wrote in the past to answer a question about stack overflow that will work with relatively simple adaptation (it sorts types with values, in this case we sort the list of compile time values).

 // 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...> > {}; 

Detection of elements outside the valid range of colors (i.e., <MIN_COLOR or >=MAX_COLOR ). If so, you squinted.

  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) > {}; 

Count how many elements there are - there should be MAX_COLOR elements. If not, you squinted.

  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) > {}; 

If none of this happened, you can initialize each of them.

The only thing you are missing is that you could leave and use the same string for two values. Since string compilation time is a pain, just check it at runtime (the number of entries in map is equal to the number of colors after its initialization).

Doing this in C ++ 03 will be harder. You do not have enough variable templates, so you have to fake them. It is a pain. mpl can help you there.

Variardic templates are available in the MSVC CTP compiler update on Nov 2012.

Here is an example of toys without checking for duplicates and without restrictions (it just checks if the number of entries in the card matches);

 #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() {} 

Replace #if 0 with #if 1 to view its compilation. Direct link if you want to play.

+4
source

Does anyone have an idea on how this can be implemented? Perhaps with templates or with the implementation of my own Enum class?

This is impossible to do. Not with std :: map, not with template meta-programming.

+2
source

I want to publish the solution that I finally got. I do not want to mark it as the final answer, because I would like to find out if we can determine line vectors at compile time.

 // // 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"); } 

Launch it on Coliru: http://coliru.stacked-crooked.com/a/e81e1af0145df99a

0
source

All Articles