Define template based map pointer in order to parse int to enum while validating range
我想提供一个简单的机制,它将int转换为枚举类型,并在此过程中提供一些错误检查。 这样可以确保int不会超出其声明的枚举范围。 我已经提出了以下函数,它使用了std :: map:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #include <map> #include <type_traits> enum A { a = 0, b, c }; enum B { d = 10, e, f }; std::map<int, A> mA; std::map<int, B> mB; template<typename T> T ParseEnum(int nVal) { std::map<int, T>* p; if (std::is_same<T, A>::value) p = &mA; //compiler error else if (std::is_same<T, B>::value) p = &mB; //compiler error return static_cast< T >(p->at(nVal)); } int _tmain(int argc, _TCHAR* argv[]) { mA.insert(std::pair<int, A>(0, A::a)); mA.insert(std::pair<int, A>(1, A::b)); mA.insert(std::pair<int, A>(2, A::c)); mB.insert(std::pair<int, B>(10, B::d)); mB.insert(std::pair<int, B>(11, B::e)); mB.insert(std::pair<int, B>(12, B::f)); try { A eA = ParseEnum<A>(1); //ok, should resolve to A::b; B eB = ParseEnum(16); //should throw an exception; } catch (std::out_of_range&) { } return 0; } |
不幸的是,我无法将映射引用分配给基于模板的映射指针,如以下编译器错误所示:
有没有办法定义这样一个基于模板的指针,还是我运气不好?
I would like provide a simple mechanism which casts an int to an enum
type and also provides some error checking along the way. This would
make sure, that the int doesn't fall out its declared enum range.
这听起来像是一个糟糕的枚举。理想情况下,从语义的角度来看,枚举应该只是一个具有固定值集的不同类型,并且它作为
您可能想要查看C ++ 11枚举类。
1 return static_cast< T >(p->at(nVal));
1 2 3 4 5 6 | std::map<int, T>::iterator find_iter = p->find(nVal); if (find_iter == p->end()) { throw std::out_of_range("illegal value"); } return static_cast< T >(find_iter->second); |
S>
(C ++ 11为
1 int _tmain(int argc, _TCHAR* argv[])
应该是标准C ++中的
1 mA.insert(std::pair<int, A>(0, A::a));
您应该使用
1 | mA.insert(std::make_pair(0, A::a)); |
现在让我们看看导致编译器错误的原因:
1
2
3
4
5
6
7
8
9
10
11
12 template<typename T> T ParseEnum(int nVal)
{
std::map<int, T> * p;
if (std::is_same<T, A>::value)
p = &mA; //compiler error
else if (std::is_same<T, B>::value)
p = &mB; //compiler error
return static_cast< T >(p->at(nVal));
}
在此函数中,
1 2 3 4 5 6 7 8 9 10 11 12 13 | // pseudo code: A ParseEnumA(int nVal) { std::map<int, A> * p; if (std::is_same<A, A>::value) p = &mA; // NOT a compiler error else if (std::is_same<A, B>::value) p = &mB; //compiler error return static_cast<A>(p->at(nVal)); } |
在第二种情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // pseudo code: B ParseEnumB(int nVal) { std::map<int, B> * p; if (std::is_same<B, A>::value) p = &mA; // compiler error else if (std::is_same<B, B>::value) p = &mB; // NOT a compiler error return static_cast(p->at(nVal)); } |
走着瞧吧?在任何一种情况下,其中一个分配都有效,另一个失败,因为
Is there a way to define such a template based pointer or am I out of
luck here?
对于非常快速和肮脏的修复,您可以使用
1 2 3 4 5 | if (std::is_same<T, A>::value) p = reinterpret_cast<std::map<int, T>*>(&mA); else if (std::is_same<T, B>::value) p = reinterpret_cast<std::map<int, T>*>(&mB); |
这是完整的程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #include <map> #include <stdexcept> #include <iostream> enum A { a = 0, b, c }; enum B { d = 10, e, f }; std::map<int, A> mA; std::map<int, B> mB; template<typename T> T ParseEnum(int nVal) { std::map<int, T>* p; if (std::is_same<T, A>::value) p = reinterpret_cast<std::map<int, T>*>(&mA); else if (std::is_same<T, B>::value) p = reinterpret_cast<std::map<int, T>*>(&mB); std::map<int, T>::iterator find_iter = p->find(nVal); if (find_iter == p->end()) { throw std::out_of_range("illegal value"); } return static_cast< T >(find_iter->second); } int main() { mA.insert(std::make_pair(0, A::a)); mA.insert(std::make_pair(1, A::b)); mA.insert(std::make_pair(2, A::c)); mB.insert(std::make_pair(10, B::d)); mB.insert(std::make_pair(11, B::e)); mB.insert(std::make_pair(12, B::f)); try { A eA = ParseEnum<A>(1); //ok, should resolve to A::b; std::cout <<"OK "; B eB = ParseEnum(16); //should throw an exception; } catch (std::out_of_range const& exc) { std::cerr << exc.what() <<" "; } } |
但问题是:这是一个很好的解决方案吗?
我不这么认为。您应该使用C ++ 11枚举类,并删除将
模板专业化更适合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //declare the ParseEnum function template template<typename T> T ParseEnum(int nVal); //specialization for when ParseEnum is instantiated with A template<> A ParseEnum<A> (int nVal) { return mA.at(nVal); } //specialization for B template<> B ParseEnum (int nVal) { return mB.at(nVal); } |
或者这可能更灵活。它创建一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //declare a struct which will hold a reference to the map we want template <typename T> struct GetMap { static std::map<int,T> ↦ }; //when instantiated with A, the map member is a reference to mA template<> std::map<int,A> &GetMap<A>::map = mA; //similarly for B and mB template<> std::map<int,B> &GetMap::map = mB; template<typename T> T ParseEnum(int nVal) { //GetMap< T >::map will be a reference to the correct map return GetMap< T >::map.at(nVal); } |
这是处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <iostream> #include <type_traits> #include <map> #include <stdexcept> enum A { a = 0, b, c }; enum B { d = 10, e, f }; template <typename T> struct MapContainer { static std::map<int, T>& getMap() { static std::map<int, T> theMap; return theMap; } }; template<typename T> T ParseEnum(int nVal) { std::map<int, T>& theMap = MapContainer< T >::getMap(); return static_cast< T >(theMap.at(nVal)); } int main(int argc, char* argv[]) { std::map<int, A>& mA = MapContainer<A>::getMap(); mA.insert(std::pair<int, A>(0, A::a)); mA.insert(std::pair<int, A>(1, A::b)); mA.insert(std::pair<int, A>(2, A::c)); std::map<int, B>& mB = MapContainer::getMap(); mB.insert(std::pair<int, B>(10, B::d)); mB.insert(std::pair<int, B>(11, B::e)); mB.insert(std::pair<int, B>(12, B::f)); try { A eA = ParseEnum<A>(1); //ok, should resolve to A::b; std::cout <<"ParseEnum<A>(1):" << eA << std::endl; B eB = ParseEnum(16); //should throw an exception; std::cout <<"ParseEnum(16):" << eB << std::endl; } catch (std::out_of_range&) { } return 0; } |
解决问题的一种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | template <typename T> struct MapPtr; template <> struct MapPtr<A> { static constexpr std::map<int, A>* const value = &mA; }; template <> struct MapPtr { static constexpr std::map<int, B>* const value = &mB; }; template<typename T> T ParseEnum(int nVal) { std::map<int, T>* ptr = MapPtr< T >::value; return static_cast< T >(ptr->at(nVal)); } |