Search code examples
c++c++20constexprstatic-assert

Enable static checks for constant evaluation


I like to have some sort of static (compile-time) check, if a type is initialised with a constant.

Below is a test code. The type C is just a test to see if/when constant-evaluation is triggered.

The type D is more to be a real example of what I want to do:

  1. Runtime: if D is initialized with the wrong value, the value might be clamped somewhat or a runtime-assertion should trigger

  2. Compiletime: if D is initialized with the wrong value, the compilation should fail.

I can't find a solution for that.

The code:

#include <cassert>
#include <type_traits>

inline void constant_assert() {}

struct C {
    constexpr C() {
        if (std::is_constant_evaluated()) {
            constant_assert();
        }
    }
};

struct D {
    constexpr D(const char v) {
        if (std::is_constant_evaluated()) {
            //static_assert(v < 10); // not callable
            if (v >= 10) {
                constant_assert();
            }
        }
        else {
            assert(v < 10);
        }
    }
};

int main() {
    C t0; // does not trigger the check
//    constexpr C t1; // triggers the check: ok

    D d{42}; // runtime assert, but should be compiletime error!
}

The above code in CE: https://godbolt.org/z/16GTx797q


Solution

  •  D d{42}; // runtime assert, but should be compiletime error!
    

    In the way you want it, it is not possible. Your method is fine - if your arguments are to be constant evaluated, do constexpr D d{42}. See explanation of is_constant_evaulated - I found https://gist.github.com/Som1Lse/5309b114accc086d24b842fd803ba9d2.


    You can use the gcc extension __builtin_constant_p calling a function with an error attribute (and optimizations maybe are needed to be enabled). This method is typically used in C by library implementations - see _FORTIFY_LEVEL in glibc.

    #include <cassert>
    #include <type_traits>
    
    __attribute__((__error__("Och nooo")))
    void undefined_function();
    
    struct D {
        constexpr D(const int v) {
            if (__builtin_constant_p(v)) {
                if (v >= 10)
                    undefined_function();
            } else {
                assert(v < 10);
            }
        }
    };
    
    int main(int argc, char *argv[]) {
        D d1{42};
    }