Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditionally static_assert if function is called constexpr

I know that there is a proposal for the constexpr() operator, but this isn't implemented in gcc/clang yet. I also know that there is a implmentation using some tricks like machine-code editing:

http://saadahmad.ca/detecting-evaluation-context-inside-constexpr-functions/

I wonder if there is a somewhat restricted solution to this:

struct F {
    constexpr F(int v) {
         if constexpr(constexpr()) {
             static_assert(v > 0);
         }
         else {
             assert(v > 0);
         }
    }
};

// ...

constexpr F f{0}; // should trigger a compile-time error

I know the static_assert couldn't be used in such a way, but this is only to clarify the problem.

like image 516
wimalopaan Avatar asked Apr 12 '19 08:04

wimalopaan


2 Answers

In your particular case you can just leave assert - it will prevent compiling when the condition is wrong since the assert handler is non-constexpr:

#include <cassert>

struct F {
    constexpr F(int v) {
         assert(v >0);
    }
};

// ...

constexpr F f1{0}; // doesn't compile in debug
constexpr F f2{1}; // compiles

However this won't trigger the compile-time error in release. It can be solved by making your own assert and adding call to some non-constepxr function:

#include <cassert>

// some non-constexpr function
void AssertConditionFailed()
{
}

#define ASSERT_WIH_COMPILE_TIME_CHECK(...) \
    assert(__VA_ARGS__); \
    if (!(__VA_ARGS__)) \
    { \
        AssertConditionFailed(); \
    }\

struct F {
    constexpr F(int v) {
         ASSERT_WIH_COMPILE_TIME_CHECK(v >0);
    }
};

// ...

constexpr F f1{0}; // doesn't compile
constexpr F f2{1}; // compiles
like image 74
Dmitry Gordon Avatar answered Sep 24 '22 06:09

Dmitry Gordon


Not in such a direct way, because static_assert will simply not be allowed there, and similarly trying to use v as a template argument will fail so no use of enable_if type solution.

For the purposes of errors, if a constexpr results in an exception, you will get a compile error.

You could use a macro such as assert (allowed since C++14) allowing it to be optimised out in release builds and keeping your original debug runtime behaviour.

constexpr int foo(int v)
{
    if (v < 0) throw std::invalid_argument("invalid v");
    return v * 2;
}

int main() {
    int a = -1;
    int a2 = foo(a); // Not evaluated at compile time
    constexpr int b = foo(2);
    constexpr int c = foo(-1); // ERROR
}
like image 42
Fire Lancer Avatar answered Sep 20 '22 06:09

Fire Lancer