【问题标题】:How can I extend a lexical cast to support enumerated types?如何扩展词法转换以支持枚举类型?
【发布时间】:2010-12-04 10:38:35
【问题描述】:

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

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 对枚举类型具有有效值;我主要将其用于简单的序列化)

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

我对模板参数进行了一些尝试,但没有提出任何建议;不必为枚举类型调用一个函数而为其他所有类型调用另一个函数,这真是太好了。

谢谢

【问题讨论】:

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


    【解决方案1】:

    您必须执行两个步骤。找到一个足够大的整数类型来存储值。您可以使用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。在我阅读下面 GMan 的答案之前,我还打算写一篇关于为什么这不在标准库中的评论。
    • boost 不再需要,因为 std::enable_if 和 std::is_enum 在 C++11 中提供了这些功能
    【解决方案2】:

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

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

    代替 Johannes get_etype 诡计。

    【讨论】:

    • 太棒了;多么方便的功能。当我问这个问题时,我真是个菜鸟……我的意思是,我还是个菜鸟,但那时我更像个菜鸟。
    猜你喜欢
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多