Альтернатива reinterpret_cast с функциями constexpr

Ниже вы найдете строковый литерал constexpr для вычисления CRC32.

Мне пришлось переосмыслить строковый литерал от char до unsigned char. Поскольку reinterpret_cast недоступен в функции constexpr, обходной путь представляет собой небольшую полезную функцию для двух дополнений вручную, но я немного разочарован этим.

Существует ли более элегантное решение для борьбы с подобными манипуляциями?

#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

Два стандарта не гарантируются стандартом; в разделе 3.9.1:

7 - [...] Представления интегральных типов должен определять значения с использованием чистой двоичной системы нумерации. [Пример: этот международный стандарт допускает 2 дополнения, 1 дополнение и подписанные представления величины для интегральных типов. - конец пример]

Таким образом, любой код, который предполагает два дополнения, должен будет выполнить соответствующие манипуляции вручную.

Тем не менее, ваша функция преобразования не нужна (и, возможно, неверна); для конверсий без подписчиков вы можете просто использовать стандартное интегральное преобразование (4.7):

2 - Если тип назначения не указан, результирующее значение представляет собой наименьшее целое без знака, сравнимое с исходным целым (по модулю 2 n где 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; 
}