使用constexpr将数字转换为字符串字面值

Convert a number to a string literal with constexpr

本文关键字:字符串 字面值 转换 数字 constexpr 使用      更新时间:2023-10-16

我正在寻找一种在编译时将数字转换为字符串文字的方法。它应该看起来像这样:

template <unsigned num>
struct num_to_string {
    constexpr static char value[] = /* ... magic goes here ... */;
};

使得num_to_string<5>::value等于"5"{'5', ''}

这对于在编译时从其他constexpr数字计算的结果生成一个字符串是很有用的。

还请注意,我只对unsigned数字感兴趣,因为这应该更容易处理。有符号版本的加分项:)

编辑:注意,这类似于c++在编译时将整型转换为字符串,但不相同。这里我明确地希望使用constexpr而不是宏来帮助泛型编程。

救急的可变模板。:)

namespace detail
{
    template<unsigned... digits>
    struct to_chars { static const char value[]; };
    template<unsigned... digits>
    constexpr char to_chars<digits...>::value[] = {('0' + digits)..., 0};
    template<unsigned rem, unsigned... digits>
    struct explode : explode<rem / 10, rem % 10, digits...> {};
    template<unsigned... digits>
    struct explode<0, digits...> : to_chars<digits...> {};
}
template<unsigned num>
struct num_to_string : detail::explode<num> {};

和往常一样,这里有一个Coliru的实例,展示了用法和(相关的)生成的程序集。


使用这种方法来支持负数是很简单的。下面是一个更通用的表单,它要求用户输入整数的类型:

namespace detail
{
    template<uint8_t... digits> struct positive_to_chars { static const char value[]; };
    template<uint8_t... digits> constexpr char positive_to_chars<digits...>::value[] = {('0' + digits)..., 0};
    template<uint8_t... digits> struct negative_to_chars { static const char value[]; };
    template<uint8_t... digits> constexpr char negative_to_chars<digits...>::value[] = {'-', ('0' + digits)..., 0};
    template<bool neg, uint8_t... digits>
    struct to_chars : positive_to_chars<digits...> {};
    template<uint8_t... digits>
    struct to_chars<true, digits...> : negative_to_chars<digits...> {};
    template<bool neg, uintmax_t rem, uint8_t... digits>
    struct explode : explode<neg, rem / 10, rem % 10, digits...> {};
    template<bool neg, uint8_t... digits>
    struct explode<neg, 0, digits...> : to_chars<neg, digits...> {};
    template<typename T>
    constexpr uintmax_t cabs(T num) { return (num < 0) ? -num : num; }
}
template<typename Integer, Integer num>
struct string_from : detail::explode<(num < 0), detail::cabs(num)> {};

的用法如下:

string_from<signed, -1>::value