如何将数字分隔符用于非类型模板参数?

Die*_*ühl 7 c++ literals c++11

我有一个模板,它采用类型的非类型模板参数unsigned long long.实例化这个模板很快就会变得混乱,因为它涉及很多数字.事实上,理想的我会用二进制表示使事情变得更糟:有多达64个01字符.如何创建视觉上可识别的参数.例如:

template <unsigned long long>
struct use_mask {
    // for this question it doesn't matter what the template actually does
};

int main() {
    use_mask<0b1001001010010010>    unreadable;    // OK, there are no binary literals
    use_mask<0b1001,0010,1001,0010> more_readable; // ... nor are there digit separators
}
Run Code Online (Sandbox Code Playgroud)

有没有办法近似后一种符号,可能在值之前和之后有一些东西?

Die*_*ühl 7

以下是我回答自己的问题:使用constexpr与字符串文字一起实现的用户定义文字,可以有效地为相关值指定解析器!有两个部分略显难看:

  1. 实际文字用双引号括起来.
  2. 在所述字符串的末尾有一个用户定义的文字.

除此之外,这种方法实际上允许指定整数文字,包括数字分隔符.我目前还不能创建一个花哨的版本来验证分隔符位于正确的位置,但这应该只是正确编程的一个小细节.下面是一个实现相应的用户定义文字的程序,允许使用类似的

use_mask<"0b0101,0101,0101,0101,0011,0101"_sep> um1;
use_mask<"0x0123,4567,89ab,cdef"_sep>           um2;
Run Code Online (Sandbox Code Playgroud)

当然,这实际上也是在使用文字.实际的文字是

"0b0101,0101,0101,0101,0011,0101"_sep
"0x0123,4567,89ab,cdef"_sep
Run Code Online (Sandbox Code Playgroud)

......它们可以完全分开使用.抛出异常的错误消息不一定像我希望的那样漂亮.还有人指出,使用用户定义文字来处理数字分隔符会排除使用其他用户定义的文字.

#include <algorithm>
#include <iostream>
#include <stdexcept>

template <unsigned long long Value>
struct use_mask {
    static constexpr unsigned long long value = Value;
};

// ------------------------------------------------------------------------

constexpr bool is_digit(char c, unsigned base)
{
    return '0' <= c && c < '0' + int(base < 10u? base: 10u);
}

constexpr bool is_hexdigit(char c)
{
    return ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
}

constexpr unsigned long long hex_value(char c)
{
    return c - (('a' <= c && c <= 'f')? 'a': 'A') + 10;
}

// ------------------------------------------------------------------------

constexpr unsigned long long decode(unsigned long long value,
                                    unsigned base,
                                    char const* str, size_t n)
{
    return n == 0
        ? value
        : (str[0] == ','
           ? decode(value, base, str + 1, n - 1)
           : (is_digit(str[0], base)
              ? decode(value * base + str[0] - '0', base, str + 1, n - 1)
              : (base == 16u && is_hexdigit(str[0])
                 ? decode(value * base + hex_value(str[0]),
                          base, str + 1, n - 1)
                 : throw "ill-formed constant with digit separators"
                 )
              )
           );
}

constexpr unsigned long long operator"" _sep(char const* value,
                                             std::size_t n)
{
    return 2 < n && value[0] == '0'
        ? ((value[1] == 'b' || value[1] == 'B')
           ? decode(0ull, 2, value + 2, n - 2)
           : ((value[1] == 'x' || value[1] == 'X')
              ? decode(0ull, 16, value + 2, n - 2)
              : decode(0ull, 8, value + 1, n - 1)))
        : decode(0ull, 10, value, n);
}

int main()
{
    std::cout << use_mask<"0b1010,1010"_sep>::value << "\n";
    std::cout << use_mask<"02,52"_sep>::value << "\n";
    std::cout << use_mask<"1,70"_sep>::value << "\n";
    std::cout << use_mask<"0xA,A"_sep>::value << "\n";
#ifdef ERROR
    std::cout << use_mask<"0xx,A"_sep>::value << "\n";
#endif

    std::cout << use_mask<"0b0101,0101,0101,0101,0011,0101"_sep>::value
              << '\n';
    std::cout << use_mask<"0x0123,4567,89ab,cdef"_sep>::value << '\n';
}
Run Code Online (Sandbox Code Playgroud)


Ric*_*ith 5

从C++ 14开始,这是有效的:

use_mask<0b1001'0010'1001'0010> its_just_what_i_wanted;
Run Code Online (Sandbox Code Playgroud)