Search code examples
c++c++17constexprif-constexpr

why this constexpr if doesn't compile


I want to simplify a code by using a constexpr function instead of multi constexpr if branches.

This is the code with the old code commented the old code compiles with msvc (vs 2017 with c++17) and clang (android ndk r20), but it fails to compile with clang 8 in for windows x64 in visual studio !

and the new code neither compiles with msvc nor clang

template <class T>
constexpr bool IsValueNegative(T value) // this function should be evaluated at runtime but it it isn't !
{
    if constexpr (std::is_integral_v<T>) // SOCKET = ULONG_PTR and linux handles are int
    {
        return value < 0;
    }
    else // HANDLE = void * and most other handles are pointers
    {
        return (intptr_t)value < 0;
    }
}

template <class T, const T null_value, bool no_negative = true>
class HandleWrapper
{
public:
    HandleWrapper() : handle(null_value)
    {}
    HandleWrapper(T h) : handle(h)
    {
        if constexpr (no_negative) // to convert INVALID_HANDLE_VALUE to nullptr
        {
            if constexpr (!IsValueNegative(null_value)) // SOCKET invalid handle is -1
            {
                if (IsValueNegative(handle)) // 
                    handle = null_value;
            }
            /*
            if constexpr (std::is_integral_v<T>)
            {
                if constexpr (null_value >= 0)
                {
                    if (handle < 0)
                        handle = null_value;
                }
            }
            else
            {
                if constexpr ((intptr_t)null_value >= 0) // clang 8 can't compile this , don't know why
                {
                    if ((intptr_t)handle < 0)
                        handle = null_value;
                }
            }
            */
        }
    }
private:
    T handle;
};

template <class T, const T null_value, bool no_negative, auto Deleter>
struct HandleHelper
{
    using pointer = HandleWrapper<T, null_value, no_negative>;
    void operator()(pointer p)
    {
        if constexpr (!no_negative)
        {
            if (!IsValueNegative(null_value) && IsValueNegative(T(p))) // pseudo handle from GetCurrentProcess or GetCurrentThread
                return;
        }
        /*
        if constexpr (!no_negative && )
        {
            if ((uintptr_t)T(p) <= 0)
            {
                std::cout << "[*] this is a pseudo handle\n";
                return;
            }
        }
        */
        Deleter(T(p));
    }
};

using Handle = std::unique_ptr<HandleWrapper<HANDLE, nullptr, true>, HandleHelper<HANDLE, nullptr, true, CloseHandle>>;
using ProcessHandle = std::unique_ptr<HandleWrapper<HANDLE, nullptr, false>, HandleHelper<HANDLE, nullptr, false, CloseHandle>>;
using ThreadHandle = ProcessHandle;

The new code fails to compile in this line :

if constexpr (!IsValueNegative(null_value)) // SOCKET invalid handle is -1

the error code from msvc is :

Error C2131 expression did not evaluate to a constant

and from clang 8 :

constexpr if condition is not a constant expression

however all the null_value is known at compile time


Solution

  • Clang gives this error when you run it:

    note: cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression
            return (intptr_t)value < 0;
    

    Casting a pointer to an integer type is not a constant expression (because it is a reinterpret_cast), so you can't use if constexpr.