如何扩展词法转换以支持枚举类型?

时间:2009-10-06 22:10:10

标签: c++ serialization enums casting lexical-cast

我有以下函数将字符串转换为数字数据类型:

template <typename T>
bool ConvertString(const std::string& theString, T& theResult)
{
    std::istringstream iss(theString);
    return !(iss >> theResult).fail();
}

但这对枚举类型不起作用,所以我做了类似的事情:

template <typename T>
bool ConvertStringToEnum(const std::string& theString, T& theResult)
{
    std::istringstream iss(theString);
    unsigned int temp;
    const bool isValid = !(iss >> temp).fail();
    theResult = static_cast<T>(temp);
    return isValid;
}

(我假设theString具有枚举类型的有效值;我主要用于简单序列化)

有没有办法创建一个结合这两者的单一功能?

我玩了一些模板参数,但没有想出任何东西;如果不必为枚举类型调用一个函数而为其他所有函数调用另一个函数,那就太好了。

谢谢

2 个答案:

答案 0 :(得分:42)

你必须做两个步骤。找到一个足以存储值的整数类型。您可以使用unsigned long,但值可能为负数。然后,您可以使用long,但值可以扩展到unsigned long的范围。所以没有真正适合它的类型。

通过使用重载解析,有一个技巧。这是它

template<typename T>
struct id { typedef T type; };

id<char[1]>::type &find_etype(int);
id<char[2]>::type &find_etype(unsigned int);
id<char[3]>::type &find_etype(long);
id<char[4]>::type &find_etype(unsigned long);

如果您的实施支持,您可以适当地更改它以涵盖long longunsigned long long。现在,传递枚举类型将优先于其他所有类型 - 这是一种可以存储它的所有值的类型。您只需将返回类型的sizeof传递给某个模板即可。

template<int> struct get_etype;
template<> struct get_etype<1> { typedef int type; };
template<> struct get_etype<2> { typedef unsigned int type; };
template<> struct get_etype<3> { typedef long type; };
template<> struct get_etype<4> { typedef unsigned long type; };

现在,您可以获得正确的类型。您现在需要的是查看某些类型是否为枚举。如何执行此操作在“C ++模板 - 完整指南”一书中有所描述,遗憾的是它是一大堆代码。所以我会使用boost is_enum。把它放在一起,看起来像

template <typename T>
typename boost::disable_if< boost::is_enum<T>, bool>::type 
ConvertString(const std::string& theString, T& theResult)
{
    std::istringstream iss(theString);
    return !(iss >> theResult).fail();
}

template <typename T>
typename boost::enable_if< boost::is_enum<T>, bool>::type 
ConvertString(const std::string& theString, T& theResult)
{
    typedef typename get_etype<sizeof find_etype(theResult)>::type 
      safe_type;

    std::istringstream iss(theString);
    safe_type temp;
    const bool isValid = !(iss >> temp).fail();
    theResult = static_cast<T>(temp);
    return isValid;
}

希望这会有所帮助。

答案 1 :(得分:10)

只是为了“完成”这个问题,在C ++ 0x中我们可以这样做:

typedef typename std::underlying_type<T>::type safe_type;

取代约翰内斯get_etype技巧。