c++castingconstexpr

How to cast from const void* in a constexpr expression?


I'm trying to reimplement memchr as constexpr (1). I haven't expected issues as I have already successfully done the same thing with strchr which is very simmilar.

However both clang and gcc refuse to cast const void* to anything else within constexpr function, which prevents me to access the actual values.

I understand that working with void* in constexpr function is weird, as we cannot do malloc and there is no way to specify arbitrary data as literal values. I'm doing this basically as a part of an excercise to rewrite as much as I can from as constexpr (2).

Still I'd like to know why this is not allowed and if there is any way around it.

Thank you!

(1) My implementation of memchr:

constexpr void const *memchr(const void *ptr, int ch, size_t count) {
    const auto block_address = static_cast<const uint8_t *>(ptr);
    const auto needle = static_cast<uint8_t>(ch);
    for (uintptr_t pos{0}; pos < count; ++pos) {
        auto byte_address = block_address + pos;
        const uint8_t value = *byte_address;
        if (needle == value) {
            return static_cast<void const *>(byte_address);
        }
    }
    return nullptr;
}

(2) The entire project on Github: https://github.com/jlanik/constexprstring


Solution

  • No, it is impossible to use void* in such a way in constant expressions. Casts from void* to other object pointer types are forbidden in constant expressions. reinterpret_cast is forbidden as well.

    This is probably intentional to make it impossible to access the object representation at compile-time.

    You cannot have a memchr with its usual signature at compile-time.

    The best that I think you can do is write the function for pointers to char and its cv-qualified versions, as well as std::byte (either as overloads or as template), instead of void*.

    For pointers to objects of other types it is going to be tricky in some cases and impossible in most cases to implement the exact semantics of memchr.

    While I am not certain that it is possible, maybe, in a templated version of memchr, one can read the underlying bytes of the objects passed-by-pointer via a std::bit_cast into a struct containing a std::byte/unsigned char array of appropriate size.