Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative to reinterpret_cast with constexpr functions

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.

like image 924
galop1n Avatar asked Jan 31 '14 15:01

galop1n


People also ask

Should you ever use Reinterpret_cast?

It is used when we want to work with bits. If we use this type of cast then it becomes a non-portable product. So, it is suggested not to use this concept unless required. It is only used to typecast any pointer to its original type.

Why is Reinterpret_cast not Constexpr?

There are very few valid uses of reinterpret_cast , most of them result in UB. Plus it is practically impossible to check if the use is valid. So reinterpret_cast is not allowed during compilation, i.e. it is not allowed in constexpr.

What is the point of Reinterpret_cast?

The reinterpret_cast allows the pointer to be treated as an integral type. The result is then bit-shifted and XORed with itself to produce a unique index (unique to a high degree of probability). The index is then truncated by a standard C-style cast to the return type of the function.

Is reinterpret cast compile time?

None of the casts happen at compile time. You only have the data at runtime, in general.


1 Answers

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; 
} 
like image 66
ecatmur Avatar answered Sep 20 '22 06:09

ecatmur