这个问题很可能是"How to map strings to enums"的第n次迭代。
我的要求更进一步,如果在有效输入范围内找不到密钥,我想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)。
有没有人知道如何实现这一点?也许使用模板,或实现我自己的Enum类?
答案 0 :(得分: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") );
它为您提供了一种C ++ 11方法,可以从编译时std::map
和运行时color
数据构建string
。
接下来将“MapEntry<colors>
列表中的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
的数量。)
在C ++ 03中执行此操作会更难。你缺乏变量模板,所以你最终不得不假装它们。这是一种痛苦。 mpl
可以帮助你。
2012年11月MSVC CTP编译器更新中提供了可变模板。
这是一个没有重复检查且没有边界检查的玩具示例(它只是检查地图条目的数量是否匹配);
#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 0
替换为#if 1
以观察其编译。 Live link如果你想玩。
答案 1 :(得分:2)
有没有人知道如何实现这一点?也许使用模板,或实现我自己的Enum类?
这是不可能的。不是使用std :: map,而是使用模板元编程。
答案 2 :(得分: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