Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I do a runtime assert in a constexpr function?

From what I understand, a constexpr function can be executed at compile time as well as runtime, depending on if the entire evaluation can be done at compile time or not.

However, you cannot overload this function to have a runtime and a compile time counterpart.

So my question is, how can I put in a runtime assert to ensure that the execution of the runtime function is passed valid parameters along with my static_assert?

like image 757
Adrian Avatar asked Sep 04 '15 15:09

Adrian


People also ask

Does constexpr compile time?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.

Is assert a runtime?

A runtime assertion is a statement that is expected to always be true at the point in the code it appears at. They are tested using PHP's internal assert() statement. If an assertion is ever FALSE it indicates an error in the code or in module or theme configuration files.

Can a constexpr function throw an exception?

Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.

Does constexpr improve performance?

Using constexpr to Improve Security, Performance and Encapsulation in C++ constexpr is a new C++11 keyword that rids you of the need to create macros and hardcoded literals. It also guarantees, under certain conditions, that objects undergo static initialization.


2 Answers

Eric Niebler covers this issue well in Assert and Constexpr in C++11, he points out that using assert in a constexpr function is not allowed in C++11 but it is allowed in C++14(As part of the Relaxing constraints on constexpr functions proposal) and provides the following snippet:

constexpr bool in_range(int val, int min, int max)
{
    assert(min <= max); // OOPS, not constexpr
    return min <= val && val <= max;
}

If we have to support C++11 then there are some alternatives. The obvious one is the using throw but this as he points out this turns what should be a unrecoverable error into a recoverable one since you can catch the exception.

He suggest some alternatives:

  1. Using throw with the noexcept specifier:

    constexpr bool in_range(int val, int min, int max) noexcept 
    {
      return (min <= max)
        ? min <= val && val <= max
        : throw std::logic_error("Assertion failed!");
    }
    

    if an exception leaves the function std::terminate will be called.

  2. Call std::quick_exit from the constructor of an exception type:

    struct assert_failure
    {
        explicit assert_failure(const char *sz)
        {
            std::fprintf(stderr, "Assertion failure: %s\n", sz);
            std::quick_exit(EXIT_FAILURE);
        }
    };
    
    constexpr bool in_range(int val, int min, int max)
    {
        return (min <= max)
          ? min <= val && val <= max
          : throw assert_failure("min > max!");
    }
    
  3. Pass a lambda expression that asserts to the constructor of an exception type:

    constexpr bool in_range(int val, int min, int max)
    {
        return (min <= max)
          ? min <= val && val <= max
          : throw assert_failure(
              []{assert(!"input not in range");}
            );
    }
    
like image 155
Shafik Yaghmour Avatar answered Sep 19 '22 12:09

Shafik Yaghmour


You can throw an exception. If an exception is thrown at compile time from a constexpr function, it basically counts as failing a static assert. If it happens at runtime, it will just be an exception as usual.

This question shows a code example where this happens: Passing constexpr objects around

Also related: What happens when an exception is thrown while computing a constexpr?

like image 40
Chris Beck Avatar answered Sep 21 '22 12:09

Chris Beck