How to make a countl_zero() but for any type?

175 Views Asked by At

I use in my project such a function for counting leading zeros, the aim for it is to work with any non container type and to return a constant expression, so that I could use it's results for other templates.

template <typename T>
consteval int CountLeadingZeros(const T& DATA) {
    T MASK{ 1 };
    int BITSN{ sizeof(T) * CHAR_BIT },
        LEADINGN{ 0 };
    MASK <<= BITSN - 1;
    for (int I = BITSN; I > 0; I--, LEADINGN++, MASK >>= 1) {
        if (DATA & MASK) {
            break;
        }
    }
    return LEADINGN;
}

But of cause it doesn't return a real const expression. If you'll try to use it, there will be an error:

    constexpr int value = CountLeadingZeros <const int> (100);

Error:

E3133 call to consteval function "fsm::CountLeadingZeros(const T &DATA) [with T=const int]" did not produce a valid constant expression

On the other hand C++20 countl_zero() works perfect and returns constexpr. But it can not work with anything except uint8_t.

Help me please to fight and win this E3133 error. How could I make my function working in compile time?

2

There are 2 best solutions below

3
abel1502 On BEST ANSWER

Don't pass const int as the template argument. Replace it with just int. Currently, MASK gets declared as const int MASK = 1, and then you try to modify it, which obviously doesn't work. Otherwise, the function should be constant-evaluatable

0
Max Cury On

One more solution:

template <typename T, typename MASK_TYPE = typename remove_const<T>::type>
constexpr int CountLeadingZeros(const T & DATA) {
    MASK_TYPE MASK{ 1 };
    int BITSN{ sizeof(T) * CHAR_BIT},
        LEADINGN{ 0 };
    MASK <<= BITSN - 1;
    for (int I = BITSN; I > 0; I--, LEADINGN++, MASK >>= 1) {
        if (DATA & MASK) {
            break;
        }
    }
    return LEADINGN;
}

Or shorter version:

template <typename T, typename MASK_TYPE = typename remove_const<T>::type>
constexpr int LeadingN(const T& DATA) {  // returns number of leading zeros in a bit representation
    int BITSN{ sizeof(T) * CHAR_BIT }, I{ BITSN };
    MASK_TYPE MASK{ 1u << BITSN - 1 };
    for (; I && !(DATA & MASK); I--, MASK >>= 1) {}
    return BITSN - I;
}

Here's the result: https://github.com/redmms/FineStream/tree/master