Enable static checks for constant evaluation

Viewed 97

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

2 Answers
 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};
}

You can just simply do this:

struct D {
  constexpr D(const char v) {
    assert(v < 10);
  }
};
  • If D is assigned the wrong value at run time, it will trigger assert at run time.
  • If D is assigned the wrong value at compile-time, then it will occur a compile-time error since assert is not satisfied.

Demo.

Related