0
    // codenz.cpp
    constexpr uint32_t Hashes[] =
    {
        // ntdll
        crc32::generate("memcpy"),

        // kernel32
        crc32::generate("MessageBoxA")
    };

// hash.hpp
#include <cstring>
#include <cstdint>

namespace crc32
{
    // Generate CRC lookup table
    template <unsigned c, int k = 8>
    struct f : f<((c & 1) ? 0xedb88320 : 0) ^ (c >> 1), k - 1> {};
    template <unsigned c> struct f<c, 0> { enum { value = c }; };

    #define A(x) B(x) B(x + 128)
    #define B(x) C(x) C(x +  64)
    #define C(x) D(x) D(x +  32)
    #define D(x) E(x) E(x +  16)
    #define E(x) F(x) F(x +   8)
    #define F(x) G(x) G(x +   4)
    #define G(x) H(x) H(x +   2)
    #define H(x) I(x) I(x +   1)
    #define I(x) f<x>::value ,

    constexpr unsigned crc_table[] = { A(0) };

    // Constexpr implementation and helpers
    constexpr uint32_t crc32_impl(const uint8_t* p, size_t len, uint32_t crc) {
        return len ?
            crc32_impl(p + 1, len - 1, (crc >> 8) ^ crc_table[(crc & 0xFF) ^ *p])
            : crc;
    }

    constexpr uint32_t crc32(const uint8_t* data, size_t length) {
        return ~crc32_impl(data, length, ~0);
    }

    constexpr size_t strlen_c(const char* str) {
        return *str ? 1 + strlen_c(str + 1) : 0;
    }

    constexpr uint32_t generate(const char* str) {
        return crc32((uint8_t*)str, strlen_c(str));
    }
}

As you can see the function itself and the array is constexpr and thus should be evaluated at compile time. The MSVC compiler spits out an error that "expression did not evaluate to a constant". Why is that?

3
  • 1
    MSVC's constexpr support has been improving over time. I wouldn't be surprised if the version you use simply doesn't handle this case, but it's hard to say without a minimal reproducible example or the compiler version. Commented Dec 3, 2017 at 20:21
  • @chris I have edited the post with a complete example. Commented Dec 3, 2017 at 20:28
  • 1
    Clang and GCC also complain, though the errors are better. Commented Dec 3, 2017 at 20:34

1 Answer 1

2

You should remove redundant casts:

// now we only need one static cast
constexpr uint32_t crc32_impl(const char * p, size_t len, uint32_t crc) {
    return len ?
        crc32_impl(p + 1, len - 1, (crc >> 8) ^ crc_table[(crc & 0xFF) ^ static_cast< unsigned char >(*p)])
        : crc;
}

constexpr uint32_t crc32(const char * data, size_t length)
{
    return ~crc32_impl(data, length, ~0);
}

// we can obtain string literal array size at one go
template<size_t V_array_items_count> constexpr uint32_t
generate(const char ( & str )[V_array_items_count])
{
    return crc32(str, V_array_items_count - 1);
}

online compiler

Alternatively if you want to keep crc32 interface to accept uint8_t or byte then you may need to build a corresponding copy array at compile time.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your suggestion, Onfortunately this doesn't resolve the core issue / question.
@Swagov3rflow What do you mean by "doesn't resolve the core issue / question". The issue is that you have those extra casts that can not be used in constant expression.
The code worked fine before your changes when using it outside of an array (it generated the value at compile time, i checked that). The "core issue" is that it doesn't work inside of an array.
@Swagov3rflow What do you mean by "it doesn't work inside of an array"? online compiler link demonstrates complete working example that still builds Hashes array.
When running the program locally with your changes (msvc compiler) the Values inside the Array are 0.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.