Alternative to reinterpret_cast with constexpr functions

c++, c++14, constexpr, crc32, reinterpret-cast

Solution

Two's complement is not guaranteed by the standard; in clause 3.9.1:

7 - [...] The representations of integral types shall define values by use of a pure binary numeration system. [Example: this International Standard permits 2's complement, 1's complement and signed magnitude representations for integral types. — end example ]

So any code that assumes two's complement is going to have to perform the appropriate manipulations manually.

That said, your conversion function is unnecessary (and possibly incorrect); for signed-to-unsigned conversions you can just use the standard integral conversion (4.7):

2 - If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two's complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). — end note ]

Corrected code, using `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; 
} 

Problem

Below, you will find a constexpr string literal to CRC32 computation. I had to reinterpret the string literal character from `char` to `unsigned char`. Because `reinterpret_cast` is not available in constexpr function, the workaround is a small utility function to Two's complement manually but i am a little disappointed with it. Does it exist a more elegant solution to deal with that kind of manipulation ? ``` #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; } ``` Edit : in that case, `static_cast<unsigned char>(*data++)` is enough.

Original source