Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++1y/14: Error handling in constexpr functions?

Tags:

c++

c++14

Suppose I want to write a C++1y/14 constexpr function that performs integer square root:

constexpr int constexpr_isqrt(int x);

I want to perform a sanity check to make sure that x is non-negative:

constexpr int constexpr_isqrt(int x)
{
    if (x < 0)
        ???

    ...
}

What should I write in ??? above?

Ideally if the function is evaluated in constant context it should cause a compile-time error, and if called at run-time with a run-time error (eg abort or thrown exception).

like image 723
Andrew Tomazos Avatar asked Mar 11 '14 09:03

Andrew Tomazos


People also ask

What is a constexpr function C++?

A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.

Can a constexpr function throw an exception?

For example, the default constructor of std::unique_ptr is constexpr, allowing constant initialization. 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.

Can std :: function be constexpr?

constexpr Containers and Algorithms of the Standard Template Library. 100 classical algorithms of the Standard Template Library are declared as constexpr . Consequently, you can sort a std::vector of ints at compile time.

Can you modify constexpr?

A const int var can be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed. A constexpr int var cannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.


Video Answer


1 Answers

You're in luck, there is a way! Even in C++11! Use exceptions:

#include <iostream>
#include <stdexcept>

constexpr int foo(int a)
{
    return (a >= 0) ? a : throw std::invalid_argument("Negative!");
}

template <int n>
struct Foo
{
};

int main()
{
    Foo<foo(1)> f1();  // fine, guaranteed compile time
    Foo<foo(-1)> f2(); // bad, compile time error
    foo(1);            // fine, not necessarily at compile time
    try
    {
        foo(-1);       // fine, definitively not at compile time
    }
    catch ( ... )
    {
    }
    return 0;
}

Demo: http://ideone.com/EMxe2K

GCC has a rather good error message for the disallowed case:

prog.cpp: In function ‘int main()’:
prog.cpp:17:12:   in constexpr expansion of ‘foo(-1)’
prog.cpp:6:63: error: expression ‘<throw-expression>’ is not a constant-expression
  return (a >= 0) ? a : throw std::invalid_argument("Negative!");

For a C++1y constexpr function that looks like

constexpr foo(int a)
{
    if ( a < 0 )
    {
        // error!
    }
    ++a;
    //something else
    return a;
}

you can use the above pattern by introducing a new function:

constexpr foo_positive(int a)
{
   ++a;
   //something else
   return a;
}

constexpr int foo(int a)
{
   return (a >= 0) ? foo_positive(a) : throw std::invalid_argument("Negative!");
}

Or you simply write

constexpr foo(int a)
{
    if ( a < 0 )
    {
        throw std::invalid_argument("Negative!");
    }
    ++a;
    //something else
    return a;
}
like image 153
stefan Avatar answered Oct 06 '22 05:10

stefan