Enforce compile-time constexpr [duplicate] - c++

This question already has answers here:
How to ensure constexpr function never called at runtime?
(5 answers)
Closed 1 year ago.
In C++11 we get constexpr:
constexpr int foo (int x) {
return x + 1;
}
Is it possible to make invocations of foo with a dynamic value of x a compile time error? That is, I want to create a foo such that one can only pass in constexpr arguments.

Replace it with a metafunction:
template <int x> struct foo { static constexpr int value = x + 1; };
Usage:
foo<12>::value

Unfortunately, there is no way guarantee that a constexpr function, even the most trivial one, will be evaluated by the compiler unless absolutely necessary. That is, unless it appears in a place where its value is required at compile time, e.g. in a template. In order to enforce the compiler to do the evaluation during compilation, you can do the following:
constexpr int foo_implementation (int x) {
return x + 1;
}
#define foo(x) std::integral_constant<int, foo_implementation(x)>::value
and then use foo in your code as usual
int f = foo(123);
The nice thing about this approach is that it guarantees compile-time evaluation, and you'll get a compilation error if you pass a run-time variable to foo:
int a = 2;
int f = foo(a); /* Error: invalid template argument for 'std::integral_constant',
expected compile-time constant expression */
The not so nice thing is that it requires a macro, but this seems currently inevitable if you want both guaranteed compile-time evaluation and pretty code. (I'd love to be proven wrong though!)

Yes, it can now be done in purely idiomatic C++, since C++20 added support for this kind of problem. You annotate a function with consteval and can be sure that it's being evaluated during compile time. https://en.cppreference.com/w/cpp/language/consteval
consteval int foo( int x ) {
return x + 1;
}
int main( int argc, char *argv[] )
{
return foo( argc ); // This will not compile
return foo( 1 ); // This works
}
Also see this godbolt.org demo in the 3 most relevant compilers.

I would use static_assert as shown in this example
#include<iostream>
constexpr int foo(int x) {
return x+1;
}
int main() {
// Works since its static
std::cout << foo(2) << std::endl;
static_assert(foo(2) || foo(2) == 0, "Not Static");
// Throws an compile error
int in = 3;
std::cout << foo(in) << std::endl;
static_assert(foo(in) || foo(in) == 0, "Not Static");
}
For more infos: http://en.cppreference.com/w/cpp/language/static_assert

Related

is_constant_evaluated() should produce constexpr variables?

I have read the std::is_constant_evaluated() definition, but I still not sure why (1) is not working with the latest GCC: error: 'x' is not a constant expression
template<auto v>
struct s
{};
constexpr void f(int x)
{
if (std::is_constant_evaluated())
{
// constexpr int z=x; (1)
// s<x> a; (2)
}
}
int main(int argc, char* argv[])
{
f(4);
//f(argc);
return 0;
}
By the standard, should that work?
Or just the GCC implementation is buggy?
Somehow can I achieve the expected behavior? Which is basically:
With branching on std::is_constant_evaluated()
if it is true: the code can use variables as constexpr (like (2))
if it is false: the code use variables as non-constexpr
UPDATE
Can I 'transport' the constexpr-essiveness information into a function? Basically to decide in f() that it was call with constexpr x or not.
UPDATE
A more complex example about what I would like to achieve: this sample should stringify the parameter in compile time if possible.
template<auto v>
struct dummy_stringify
{
static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};
constexpr void f(int x)
{
if (std::is_constant_evaluated())
{
std::puts("A compile time calculation:");
//std::puts(dummy_stringify<x>::str);
} else
{
std::cout<<"A runtime calculation:"<<std::endl;
std::cout<<x<<std::endl;
}
}
int main(int argc, char* argv[])
{
f(4);
f(argc);
return 0;
}
x is not a constant expression, no matter how f itself is evaluated. That's a regular if right there (how is_constant_evaluated is meant to be used). It's not a discarded branch, so it has to contain well-formed code even when f is not constant evaluated. When x won't be a constant expression the function will still contain that (unexecuted) branch, and it will attempt to use x where a constant expression is required. That's plain ill-formed.
GCC is very much correct not to accept it.
The fundamental issue here is that, even with a constexpr (or even consteval) function being called during constant evaluation (and under an is_constant_evaluated check), there is still only one function shared among all argument values. You therefore can’t ever use a function parameter as a constant expression (even if the call with that parameter is a constant expression). If you want a constant-expression parameter, it has to be a template parameter.
UPDATE
I have found a solution with a little helper class (of course one can use std::integral_constant)
template<auto val_>
struct val
{
static constexpr auto value=val_;
};
template<auto v>
struct dummy_stringify
{
static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};
#include <iostream>
using namespace std;
template<class T>
constexpr void f(T x)
{
if constexpr(requires{ T::value; })
{
std::puts("A compile time calculation:");
std::puts(dummy_stringify<T::value>::str);
} else
{
std::cout<<"A runtime calculation:"<<std::endl;
std::cout<<x<<std::endl;
}
}
int main(int argc, char* argv[])
{
f(val<4>{});
f(argc);
return 0;
}
It can be improved with a template<char...> auto operator""_v(); to f(4_v)

Nesting contexpr functions error

The follwing code doesn't compile with g++/clang++.
constexpr int bar(int v) {
if (v > 0){
return v * 2;
}
return 2;
}
constexpr int foo(const int v) {
constexpr auto x = bar(v); // error
return v;
}
int main() {
constexpr auto a = foo(1);
constexpr auto b = bar(1); // ok
}
The error message is: x must be initailized by a constant expression
But from the line (ok) you see that bar() is constexpr.
if I change the body of foo() to
constexpr int foo(const int v) {
return bar(v);
}
its ok!
I don't get this clear, why the first form isn't possilble.
I used g++-6.2.1, g++-7.0.0 and clang++-3.9.0
The fix is this
constexpr int foo(const int v) {
auto x = bar(v);
return v;
}
The keyword constexpr means two very slightly different things. A constexpr variable must be evaluated at compile time, whereas a constexpr function must be possible to evaluate at compile time. There is nothing to prevent you from calling foo at runtime. This means...
The argument v is not necessarily constexpr.
When bar is called with b the answer might not be constexpr.
The result of cannot be stored in a constexpr variable as it might not be constexpr.
If foo is called at compile time then x is not stored, it is a temporary variable within the compiler, so making it constexpr doesn't make any sense.
The constexpr'ness of x can only make sense if foo is evaluated at runtime, in which case it cannot be constexpr, which cases an error.

static_cast on custom class causes copy assignment to fail

I expected the following program to print "11" but it actually prints "01" so it seems like the first assignment fails.
struct A
{
A(int i = 0) : i_(i) {}
int i_;
};
int main()
{
A x(1);
A y;
static_cast<A>(y) = x; // *** Fails to assign ***
std::printf("%i", y.i_);
y = x;
std::printf("%i", y.i_);
}
If I use a primitive type likeint instead of A then int x = 1; int y; static_cast<int>(y) = x; does assign the value 1 to x. Is there some way I can get it to work for custom types? I tried adding operator A() { return *this; } to struct A but that didn't work.
Obviously this is a stupid program but the problem arises in a template function where I have static_cast<std::remove_const<T>::type>(y) = x and it was working fine for primitive types but just now failed for a custom type.
As with any cast, static_cast<A>(y) is a temporary copy of y. You can cast to a reference type instead (static_cast<A&>(y)); more generally, you could achieve this with std::add_lvalue_reference.
For the more specific example you described, you'll need const_cast rather than static_cast, but the basic principle is the same.
Here's an example that compiles, but has UB because of the modification of a const object (and thus returns 0, not 42). Without knowing more about what you're trying to do, I shan't attempt to disguise that for the purposes of this example:
#include <iostream>
#include <type_traits>
template <typename T>
T foo(T val)
{
T x{};
using not_const = typename std::remove_const<T>::type;
using ref_type = typename std::add_lvalue_reference<not_const>::type;
const_cast<ref_type>(x) = val;
return x;
}
int main()
{
std::cout << foo<const int>(42) << '\n';
}

C++11 function definitions with auto keyword

I am curious about using auto keyword in C++11.
for function definition, you must write the return type of function:
auto f (int x) -> int { return x + 3; }; //success
auto f (int x) { return x + 3; }; //fail
but in this example the both of them will work:
auto f = [](int x) { return x + 3; }; //expect a failure but it works
auto f = [](int x) -> int { return x + 3; }; // this is expected code
thanks.
In C++11, a lambda expression can omit its return type if it can deduce the exact one without ambiguity. However, this rule doesn't apply to the regular function.
int f() { return 0; } // a legal C++ function
auto f() -> int { return 0; } // a legal C++ function only in C++11
auto f() { return 0; } // an illegal C++ function even if in C++11
If you are required to specify the return type after "->" (as it is in C+11) - what is the point to have "auto" in a function declaration? This is as if to say: "This function can return anything, but actually it is only this type". Uh?
Then if you are not required to specify the return type after "->" (as it is in C++14): imagine you don't have the source code of the function, but object file and the header. How do you know what the function returns (the return type)?
So far it seems that "auto" in a function declaration is yet another way to write non-readable code. As if in C++ there are not enough ways to do it already.
While inside function body "auto" is a nice "syntax sugar".
Or am I missing something?

Return value from local scope?

Bumped into some code like this in our code base... which made me worried.
int foo(int a); // Forward declaration.
int baz() {
int result = {
int a = dosomestuff();
foo(a);
} ? 0 : -1;
return result;
}
Is the behavior of this code well-defined?
Will it really work, that result variable gets loaded with 0 or -1 depending on the return value of foo(a)?
For interest: The code was not written like that originally - however, it is what I imagine this innocent-looking macro will roll out to...
int foo(int a); // Forward declaration.
#define BAR() { int a = dosomestuff(); foo(a); }
int baz() {
int result = BAR() ? 0 : -1;
return result;
}
This is a GCC extension to C called 'statement expressions': http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
The key thing is that a statement expression returns the last thing it does as the value of the expression:
The last thing in the compound statement should be an expression followed by a semicolon; the value of this subexpression serves as the value of the entire construct.
In your example, that would be whatever foo(a) returns.
However the block must be enclosed in parens for GCC to accept the syntax.
int foo(); // Forward declaration.
int baz() {
int result = ({
int a = dosomestuff();
foo(a);
}) ? 0 : -1;
return result;
}
I'm unaware of any other compiler that supports this.
You'd have to consult your compiler documentation. This construct is not allowed in standard C or standard C++.
It's trivial to clean this up however, e.g.
int baz()
{
int result;
{
int a = dosomestuff();
result = foo(a)? 0: -1;
}
return result;
}
I do not know of a single compiler that will accept that. Additionally, you'd be better off doing this:
int foo();
int dosomestuff();
int baz()
{
int result = foo(dosomestuff()) ? 0 : -1;
return result;
}
It's not standard C++.
In standard C++, write
bool baz() { return !foo( dosomestuff() ); }
That's it.
Because it's a non-pointer simple type, the exact value will be returned and so the return behavior is defined. That block is... really strange though, and I'm surprised there's a C compiler that won't choke on it out there.
With C++11 you can get pretty close:
int foo(int a); // Forward declaration.
int baz() {
int result = []{
int a = dosomestuff();
return foo(a);
}() ? 0 : -1;
return result;
}