c++ - 十六进制字符到 std::string 的 constexpr 转换

标签 c++ string c++11 type-conversion

我有很多这样的字符串:

"343536"_hex

我想将其转换成相应的字节串。我正在使用 C++11 并定义了一个用户定义的字符串文字以将它们转换为十六进制字符串。但是,我目前拥有的转换不能作为我正在寻找的 constexpr 进行评估。特别是我想使用这样的东西,但作为 constexpr:

std::string operator "" _hex(const char *s, std::size_t slen )
{
    std::string str;
    str.reserve(slen);
    char ch[3];
    unsigned long num;
    ch[2] = '\0';

    for ( ; slen; slen -= 2, s += 2) {
        ch[0] = s[0];
        ch[1] = s[1];
        num = strtoul(ch, NULL, 16);
        str.push_back(num);
    }
    return str;
}

试驾

int main()
{
    std::string src{"653467740035"_hex};
    for (const auto &ch : src)
        std::cout << std::hex << std::setw(2) << std::setfill('0') 
                  << (unsigned)ch << '\n';
}

示例输出

65
34
67
74
00
35

问题

非常非常清楚我要问的是: 如何编写可在编译时作为 constexpr 求值的这种类型的 C++11 字符串文字转换?

最佳答案

为了实现您想要做的事情,您将需要一些编译时间 stringconstexpr 兼容的类.虽然没有这样一个标准的东西。我可以看到一些接近它的东西:

为了简化所有这些,让我们使用一个过度简化的 string_literal类(class)。请注意,上面描述的一些类有尾随 \0靠近std::string , 但我们不会费心添加一个。

template<std::size_t N>
struct string_literal
{
    char data[N];
};

我们还将提供operator+用于串联。解释它是如何工作的需要一些时间,而且它与问题无关。假设它只是一些模板魔法( std::integer_sequence 是一个 C++14 实用程序,但可以在 C++11 中实现):

template<std::size_t N1, std::size_t N2, std::size_t... Ind1, std::size_t... Ind2>
constexpr auto concatenate(string_literal<N1> lhs, string_literal<N2> rhs,
                   std::index_sequence<Ind1...>, std::index_sequence<Ind2...>)
    -> string_literal<N1+N2>
{
    return { lhs.data[Ind1]... , rhs.data[Ind2]... };
}

template<std::size_t N1, std::size_t N2>
constexpr auto operator+(string_literal<N1> lhs, string_literal<N2> rhs)
    -> string_literal<N1+N2>
{
    using Indices1 = std::make_index_sequence<N1>;
    using Indices2 = std::make_index_sequence<N2>;
    return concatenate(lhs, rhs, Indices1{}, Indices2{});
}

您可以使用模板用户定义的文字(使用 char... )摆脱字符串文字并获得更漂亮的文字( 1234_hex 而不是 "1234"_hex ):

template<char... Chars>
auto operator "" _hex()
    -> string_literal<sizeof...(Chars)/2>
{
    return process<Chars...>();
}

现在,您所需要的只是一个可以成对处理字符的函数。一个通用的和“完成”条件的重载。请注意 enable_if_t需要避免模棱两可的函数调用(这是 C++14,但您可以在 C++11 中将其替换为 typename std::enable_if<...>::type)。将字符转换为等效数字的“真正”工作是在 process 中完成的仅接受两个模板参数的重载。

template<char C1, char C2>
constexpr auto process()
    -> string_literal<1>
{
    return { 16 * (C1 - '0') + (C2 - '0') };
}

template<char C1, char C2, char... Rest,
         typename = std::enable_if_t< (sizeof...(Rest) > 0), void >>
constexpr auto process()
    -> string_literal<sizeof...(Rest)/2 + 1>
{
    return process<C1, C2>() + process<Rest...>();
}

您可以添加更多检查以确保始终存在偶数个字符或确保没有任何不良字符。我提供的代码使用了 C++14 标准库中的一些功能,但我确保只使用可以在需要时在 C++11 中轻松重新实现的功能。请注意,由于对 constexpr 的放宽限制,您可能可以使用 C++14 编写更易于阅读的程序。功能。

Here是一个工作的 C++14 示例,包含所有上述函数和类。我确保您的测试程序仍然有效(我只是在循环中将 scr 替换为 scr.data,因为我们使用了经过精心设计的 string_literal 类)。

关于c++ - 十六进制字符到 std::string 的 constexpr 转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25174059/

相关文章:

c++ - 编译 Protocol Buffer 生成的源文件时是否有更好的方法来解决警告?

c++ - 什么需要特殊的异常类?

c - 如何使用引用运算符在 C 中获取指向字符串的指针?

c++ - 引用模板化类/对象的数据成员

c++ - 我应该删除 move 构造函数和智能指针的 move 分配吗?

c++ - 自己的 C++11 随机数分布实现

c++ - 详尽(暴力)算法改进

c++ - 将python字典翻译成C++

java - 字节数组到字符串并反转 : recoverded byte array is NOT match to original byte array in Java

c++ - 在Windows窗体应用程序的点击功能中使用字符串