使用constexpr函数代替reinterpret_cast的替代方案

13

下面是一个用于计算CRC32的constexpr字符串字面值。

我不得不将字符串字面值中的字符从char重新解释为unsigned char。因为在constexpr函数中不允许使用reinterpret_cast,所以解决方法是手动进行二进制补码转换,但我对此感到有些失望。

是否存在更优雅的解决方案来处理这种操作?

#include <iostream>

class Crc32Gen {
    uint32_t m_[256] {};

    static constexpr unsigned char reinterpret_cast_schar_to_uchar( char v ) {
        return v>=0 ? v : ~(v-1);
    }
public:
    // algorithm from http://create.stephan-brumme.com/crc32/#sarwate
    constexpr Crc32Gen() {
        constexpr uint32_t polynomial = 0xEDB88320;
        for (unsigned int i = 0; i <= 0xFF; i++) { 
            uint32_t crc = i; 
            for (unsigned int j = 0; j < 8; j++) 
                crc = (crc >> 1) ^ (-int(crc & 1) & polynomial);
            m_[i] = crc;
        }
    }

    constexpr uint32_t operator()( const char* data ) const { 
        uint32_t crc = ~0; 
        while (auto c = reinterpret_cast_schar_to_uchar(*data++))
            crc = (crc >> 8) ^ m_[(crc & 0xFF) ^ c];
        return ~crc; 
    } 
};
constexpr Crc32Gen const crc32Gen_;

int main() {
    constexpr auto const val = crc32Gen_( "The character code for É is greater than 127" );
    std::cout << std::hex << val << std::endl;
}

编辑:在这种情况下,static_cast<unsigned char>(*data++)就足够了。

1个回答

9

标准不保证使用二进制补码;在第3.9.1款中:

7 - [...] 整型的表示必须使用纯二进制计数系统来定义值。[例子:这个国际标准允许使用2的补码、1的补码和有符号幅值表示整型。— 结束例子]

因此,任何假定使用两个补码的代码都必须手动执行相应的操作。

话虽如此,您的转换函数是不必要的(并且可能不正确);对于有符号到无符号的转换,您可以直接使用标准整数转换(4.7):

2 - 如果目标类型是无符号的,则结果值是源整数模除2n得到的最小无符号整数(其中n是用于表示无符号类型的位数)。[注意:在二进制补码表示中,此转换是概念性的,并且没有位模式的变化(如果没有截断)。— 结束注释]

使用static_cast进行更正的代码:

constexpr uint32_t operator()( const char* data ) const { 
    uint32_t crc = ~0; 
    while (auto c = static_cast<unsigned char>(*data++))
        crc = (crc >> 8) ^ m_[(crc & 0xFF) ^ c];
    return ~crc; 
} 

想了想,我可以将128添加到字符中,并安全地在m_中进行索引。 - galop1n
1
@galop1n,你可以直接从char转换为unsigned char - 参见上文。 - ecatmur
5
加上128这一操作假设了char是一个有符号的8位二进制补码类型,但是这些属性并未被标准所保证。 - Casey
4
@galop1n,但是CHAR_BIT可以大于8;它只需要至少为8(来自C语言5.2.4.2.1,在3.9.1p3中引用)。 - ecatmur
1
@galop1n:此外,无法确定char是有符号还是无符号的。在gcc中,实际上有一个标志来控制这个属性。如果您要添加128并且它最初是无符号的,则对于足够大的值,您将只会溢出(并包装)。 - Matthieu M.
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接