C++17 constexpr everything, or as much as the compiler can
solarianprogrammer.com
solarianprogrammer.com
int square(int x) { return x * x; }
const y = square(3); // evaluated at compile time
int bar() {
int[square(2)] array; // evaluated at compile time
return square(3); // evaluated at run time
}
If a value is needed at compile time, and the function cannot be evaluated at compiler time (i.e. it relies on things like global variables not known at compile time) then a compilation error is issued.There isn't any ambiguity about whether it is evaluated at runtime or compile time - there is no "fall back" to run time if it can't be done at compile time.
D is quite specific what computations occur at compile time and what at runtime. It's very much up to the user in how he sets it up.
Evaluating functions at compile time has another wrinkle in D - it is path dependent:
int square(int x) {
if (x < 100)
return x * x;
printf("oops\n");
return 0;
}
const int x = square(3); // works
const int y = square(100); // fails at compile time
I.e. the path taken through the function has to be pure, not every part of the function.An early example was someone wrote a ray tracer in D that ran and generated output completely at compile time.
A more useful example is D's regex package can build the engine at compile time, thereby emitting a custom engine for the regex, instead of at runtime.
It would seem the startup cost of building a regexp engine would really only matter for, say, command-line programs (i.e., short-lived, run-once programs). For a web application the overhead would be negligible.
Again, don't get me wrong, but it just seems to be more of a "neat!" thing than a "we _need_ this for reasons X, Y, and Z."
So, what it _really_ provides is runtime performance.
int bar=foo();
This runs foo() at runtime: int bar;
void main(){
bar=foo();
}I don't get this. Why? Doesn't making a variable constexpr ensure it is a compile time value, or it will fail to compile?
It ensures that the variable could be evaluated at compile time, where "could be evaluated at compile time" means "conforms to the rules set forth in the C++ standard". Whether or not it is computed at compile time is dependent on the compiler.
Why couldn't a variable without constexpr be evaluated at compile-time? Nobody ever explains this.
It might not be a great reason (some code becomes a keyword soup with all the pointless boilerplate, we really need a DWIM[1] keyword).
[1] Declare With Implicit Meaning of course.
> It is expected that some corners of the language will never be available at compile time, for example I/O. So you need a keyword to declare a function to be callable a compile time, otherwise its constexpressness would depend on its implementation details and wouldn't be possible to check it in isolation.
Don't compilers already require the implementation of a function to be there in order to evaluate the code at compile-time? And don't they already have to verify that it can indeed be called at compile-time? I don't really get what the constexpr flag helps the compiler. It could just assume everything is constexpr implicitly unless proven otherwise.
Meaning its entire purpose is to just be an easier version of a compile-time unit-test like static_assert(f(1) == 2)?
So you're saying functions that have side-effects like I/O can actually perform those operations at compile-time if marked with constexpr?
'auto' would've been an example, but that also got removed, because they realized it was useless. I don't get how constexpr is any different.
"... it serves as a compiler directive that suggests (but does not require) that the compiler substitute the body of the function inline by performing inline expansion, i.e. by inserting the function code at the address of each function call, thereby saving the overhead of a function call."
> [7.1.2] [dcl.fct.spec] A function declaration with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by shall still be respected
https://stackoverflow.com/questions/38879475/why-cant-conste...
Quoting Ben Voigt: "What constexpr does is provide guarantees on what data-flow analysis a compliant compiler is required to do to detect1 compile-time-computable expressions, and also allow the programmer to express that intent so that they get a diagnostic if they accidentally do something that cannot be precomputed."
inline means you CAN define a function in multiple translation units without violating ODR
no guarantees in the other direction
namespace detail {
template<class T, T val>
constexpr T eval() { return val; }
};
#define FORCE_COMPILE_TIME(x) detail::eval<decltype(x), x>()BTW, you can also enforce compile time evaluation like this:
enum { aux1 = X };
use(aux1);
But that's two lines, I know. And, of course, it is still a hack ('enum'? What?). enum aux1 = X;Here's a better idea, if you really need a compile time value in your program you calculate it yourself (or just run it at program startup and cache the value).
In C++, to debug failed constexpr’s, often, you have to make it a non-constexpr and debug at runtime.
In the context of the linked article, take the first example and modify it, by reading a number at runtime with std::cin and call factorial or fibonacci with this number as an argument.
Something like this https://godbolt.org/g/CT5M6f
A compile time debugger for the CTFE interpreter is on our extended feature list for the current interpreter rewrite, but it's less of a priority atm.
I prefer to unit test my constexpr logic with a bunch of static assertions in a cpp file. You could put them in with the code being debugged, but having them separate keeps the header files lighter weight with no real downside assuming you have CI or something set up.
So you can use regular tests/debugging to debug your function.
that's up to the compiler actually. For instance, in debug mode visual studio skips constexpr and does everything at runtime (which sucks if you depend on stuff being constexpr).
https://tour.dlang.org/tour/en/gems/compile-time-function-ev...
A constexpr is a constant that is evaluated from a code at compile time, which is essentially just macros. Right?
A general preprocessor that just do language agnostic code manipulations are not difficult but rather useful. Much complicated syntax sugar I see in the recent language development become unnecessary if a general preprocessor is in place (where syntactic sugar belong). I wonder why it is often not used.
It's rather a constant expression, which is quite a bit more than a mere constant.
> [...] which is essentially just macros. Right?
Macros are basically just text replacement. You can't write a macro and expect most compilers to execute the expressions and code inside the macro at compile-time. Some compilers may still to do that, but it's not very common for more complicated stuff. With constexpr you get a guarantee that the expression can be evaluated at compile-time and it gives quite a good hint to the optimizer to spend more time optimizing that portion of the code.
I did not even know such a thing existed. Thank you!
Yes, we are building a substantial embedded system with (a constrained subset of; no heap, but some templates for example) C++ 11. Heretical, I understand.
The compile times are much better, too. Can't wait to get a Threadripper CPU which should push the build even closer to feeling instant.
max-inline-recursive-depth
max-inline-recursive-depth-auto
Specifies the maximum recursion depth used for recursive inlining.
For functions declared inline, --param max-inline-recursive-depth is taken into
account. For functions not declared inline, recursive inlining happens only when
-finline-functions (included in -O3) is enabled and --param
max-inline-recursive-depth-auto is used. The default value is 8.Yet another C++ innovation that solves a problem that no one actually has.
Of course it is possible. Just consider all the registers, memory locations changed during the execution of such functions. Such side effects can affect behaviour of other code in the presence of compiler bugs.
Write a table generator program, have output create a table, splice in the order into make and/or make replacement.
Sure it's three extra steps, but it's small steps that can be debugged. It also doesn't require any advanced compiler tricks that may or may not actually run at compile.
Your comment makes no sense, and shows some confusion. C++ is a programming language. That's what's being discussed here. You, on the other hand, are talking about build systems and how sofrware projects may be configured by some people. That is besides the point and actually completely misses the very topic being discussed. It's entirely irrelevant if you can fill a whole header with #define or write a convoluted macro with m4 to write it for you. That's not how a programming language like C++ implements compile-time constant expressions. That's acvomplished with C++'s support for constexpr.
'extern const int *table;'
Having a compile-time program create table would guarantee that it's created at compile time. Having a constexpr does not guarantee it would be done at compile-time, it silently degrades to run-time. If verifying requires an assembly listing, I see it less useful than the currently available tools.
Yes, all of them.
Because 'make' and/or 'make' replacements have absolutely zero to do with a compiler. It is a build automation tool that essentially determines which target needs to be built based on which preceding targets have been altered. This has zero to do with what a compiler does.
Example: for the credit dept of a now-ex investment bank many moons ago we had a set of blessed (including with the correct correlations) random numbers pre-computed and baked in via code generation.
I discovered that I could actually generate numbers faster at run-time with highly-tuned code because of the high cost of paging in the large-precompiled numbers array across the network.
I am not saying there is no solution to these problem, of course there is. All I said is that it makes bootstrapping a system much harder than it would otherwise have been with constexpr. Many devs avoid those issues because dependencies rarely change and once you fixed all issues, it will most likely stay stable.
Mature and "made for embedded" projects tend to be better since cross compiling have been taken into account in each steps of the pipeline. But if you start pulling random code from the internet, expect the worst.
In such circumstances, I find myself writing the table generator in shell script, REXX, or some such convenient language on the host system.
Is this true? Where do you see Clang emit a diagnostic in the example in the given article? (https://godbolt.org/g/HKcPFT)
But how does it lead to better feedback? constexpr is purely suggesting an optimization that the compiler was already allowed to do anyway, and which it can still refuse to perform even with the keyword.
If the programmer's goal is to ensure compile-time evaluation is guaranteed, constexpr won't cut it, since the compiler can (and compilers currently do) silently fall back to run-time evaluation.
Alternatively, if the programmer's goal is to ensure compile-time evaluation is possible, constexpr still doesn't provide any value, since that's already obvious from the compiler analyzing the body of the function and noticing, say, that fopen() is getting called (and the compiler already has to do this anyway).
The situation is very critically different from const, too. Adding 'const' to a method that was previously non-const, even when it is legal and compiles perfectly fine, can entirely change the semantics of the code, and hence you want that decision to be explicit, not implicit. This isn't the case with constexpr.
Because you can use static_asset (only accepts constexprs) and in C++17 static-if (a first class cousin to #if)
I don't see how the C++ community will benefit from those, since the type of code that benefits is normally done in other languages. But I am certainly less creative than a community.