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

时间:2013-09-23 04:11:54

标签: c++ c++11 literals

我有一个模板,它采用类型为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
}

有没有办法近似后一种表示法,可能还有值之前和之后的一些东西?

5 个答案:

答案 0 :(得分:7)

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

  1. 实际文字用双引号括起来。
  2. 在所述字符串的末尾有一个用户定义的文字。
  3. 除此之外,这种方法实际上允许指定整数文字,包括数字分隔符。我目前还不能创建一个花哨的版本来验证分隔符位于正确的位置,但这应该只是正确编程的一个小细节。下面是一个实现相应的用户定义文字的程序,它允许像

    这样的用途
    use_mask<"0b0101,0101,0101,0101,0011,0101"_sep> um1;
    use_mask<"0x0123,4567,89ab,cdef"_sep>           um2;
    

    当然,这实际上也是要使用文字。实际的文字是

    "0b0101,0101,0101,0101,0011,0101"_sep
    "0x0123,4567,89ab,cdef"_sep
    

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

    #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';
    }
    

答案 1 :(得分:5)

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

use_mask<0b1001'0010'1001'0010> its_just_what_i_wanted;

答案 2 :(得分:1)

不完美,因为它需要每个二进制数字之间的逗号,但允许任何数量的空白空间和任何二进制数字的任何const expr - 所以例如0xFFFF FFFF FFFF FFFF应该是:

use_mask<bit64<
 1,1,1,1,  1,1,1,1,  1,1,1,1,  1,1,1,1,
 1,1,1,1,  1,1,1,1,  1,1,1,1,  1,1,1,1,
 1,1,1,1,  1,1,1,1,  1,1,1,1,  1,1,1,1,
 1,1,1,1,  1,1,1,1,  1,1,1,1,  1,1,1,1
>::VAL>

代码:

template <
    bool b3F,bool b3E,bool b3D,bool b3C,bool b3B,bool b3A,bool b39,bool b38,
    bool b37,bool b36,bool b35,bool b34,bool b33,bool b32,bool b31,bool b30,
    bool b2F,bool b2E,bool b2D,bool b2C,bool b2B,bool b2A,bool b29,bool b28,
    bool b27,bool b26,bool b25,bool b24,bool b23,bool b22,bool b21,bool b20,
    bool b1F,bool b1E,bool b1D,bool b1C,bool b1B,bool b1A,bool b19,bool b18,
    bool b17,bool b16,bool b15,bool b14,bool b13,bool b12,bool b11,bool b10,
    bool b0F,bool b0E,bool b0D,bool b0C,bool b0B,bool b0A,bool b09,bool b08,
    bool b07,bool b06,bool b05,bool b04,bool b03,bool b02,bool b01,bool b00
>
class bit64
{
public:
    static const unsigned long long VAL=0UL
        |((unsigned long long)b00<< 0)|((unsigned long long)b01<< 1)|((unsigned long long)b02<< 2)|((unsigned long long)b03<< 3)
        |((unsigned long long)b04<< 4)|((unsigned long long)b05<< 5)|((unsigned long long)b06<< 6)|((unsigned long long)b07<< 7)
        |((unsigned long long)b08<< 8)|((unsigned long long)b09<< 9)|((unsigned long long)b0A<<10)|((unsigned long long)b0B<<11)
        |((unsigned long long)b0C<<12)|((unsigned long long)b0D<<13)|((unsigned long long)b0E<<14)|((unsigned long long)b0F<<15)
        |((unsigned long long)b10<<16)|((unsigned long long)b11<<17)|((unsigned long long)b12<<18)|((unsigned long long)b13<<19)
        |((unsigned long long)b14<<20)|((unsigned long long)b15<<21)|((unsigned long long)b16<<22)|((unsigned long long)b17<<23)
        |((unsigned long long)b18<<24)|((unsigned long long)b19<<25)|((unsigned long long)b1A<<26)|((unsigned long long)b1B<<27)
        |((unsigned long long)b1C<<28)|((unsigned long long)b1D<<29)|((unsigned long long)b1E<<30)|((unsigned long long)b2F<<31)
        |((unsigned long long)b20<<32)|((unsigned long long)b21<<33)|((unsigned long long)b22<<34)|((unsigned long long)b23<<35)
        |((unsigned long long)b24<<36)|((unsigned long long)b25<<37)|((unsigned long long)b26<<38)|((unsigned long long)b27<<39)
        |((unsigned long long)b28<<40)|((unsigned long long)b29<<41)|((unsigned long long)b2A<<42)|((unsigned long long)b2B<<43)
        |((unsigned long long)b2C<<44)|((unsigned long long)b2D<<45)|((unsigned long long)b2E<<46)|((unsigned long long)b3F<<47)
        |((unsigned long long)b30<<48)|((unsigned long long)b31<<49)|((unsigned long long)b32<<50)|((unsigned long long)b33<<51)
        |((unsigned long long)b34<<52)|((unsigned long long)b35<<53)|((unsigned long long)b36<<54)|((unsigned long long)b37<<55)
        |((unsigned long long)b38<<56)|((unsigned long long)b39<<57)|((unsigned long long)b3A<<58)|((unsigned long long)b3B<<59)
        |((unsigned long long)b3C<<60)|((unsigned long long)b3D<<61)|((unsigned long long)b3E<<62)|((unsigned long long)b3F<<63)
        ;
};

对于支持它的编译器来说,这也可能是一个constexpr

答案 3 :(得分:0)

另一种方法是使用Macro:

#define ULongLongHex(p1, p2, p3, p4) (0x ## p1 ## p2 ## p3 ## p4)

ULongLongHex(0123,4567,89ab,cdef)

但没有检查。 :(

答案 4 :(得分:0)

我这里没有C ++编译器进行实验,但你可以尝试一下这些方法(使用模板形式的UDL):

template<char... chars>
constexpr unsigned long long operator"" _bin();

template<char... chars>
constexpr unsigned long long operator"" _bin<'0', chars...>() {
  // return correctly shifted calculated value.
}

template<char... chars>
constexpr unsigned long long operator"" _bin<'1', chars...>() {
  // return correctly shifted calculated value.
}

template<char... chars>
constexpr unsigned long long operator"" _bin<'_', chars...>() {
  // simply forward to rest
}

很抱歉因为不够具体,我只是没有这里的工具,但我已经看到UDL解析这样做了。