
C++17 constexpr everything, or as much as the compiler can - AlexeyBrin
https://solarianprogrammer.com/2017/12/27/cpp-17-constexpr-everything-as-much-as-the-compiler-can/
======
WalterBright
D doesn't need constexpr. It's much simpler - any function whose value is
needed at compile time is evaluated at compile time. For example:

    
    
        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.

~~~
soulbadguy
How does D deal with cases where the computation might be plaform dependent
(like floating point arth. or bit kung-fu+ endianess) ? An more generally,
what happens when a computation "can" be done at compile time , but "needs"
(for correctness or ressource ) done at runtime ?

~~~
WalterBright
The same way constant folding is done in any compiler. You try to emulate the
math on the target.

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.

------
andrepd
>In conclusion, at the time of this writing, you need to inspect the generated
code of your compiler, if you want to be sure that something is really
calculated at compile time.

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?

~~~
mnarayan01
> Doesn't making a variable constexpr ensure it is a compile time value

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.

~~~
mehrdadn
> It ensures that the variable _could_ be evaluated at compile time

Why couldn't a variable _without_ constexpr be evaluated at compile-time?
Nobody ever explains this.

~~~
gpderetta
The option was discussed during standardization. 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.

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.

~~~
mehrdadn
(EDIT to my last comment since I can't actually edit it -- I'm not sure if I
misinterpreted your comment earlier or if you updated yours, but this is my
updated reply.)

> 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.

~~~
StephanTLavavej
constexpr isn't just a claim about today - it's a promise for the future. That
is, the compiler can certainly notice that a function can be evaluated at
compile time, and indeed optimizers will often perform enough inlining and
constant propagation to boil down function calls to constant values in the
binary. The constexpr keyword serves two purposes: it claims that the
implementation is usable in constant expressions today (therefore allowing
compilers to diagnose (i.e. emit compiler errors for) things that can't be
done at compiletime, like I/O), and it promises that the implementation won't
change in the future to be hostile to compile-time evaluation. This promise is
important - otherwise, users could take a dependency on a function's current
behavior (e.g. by using its result as an array bound, or as a template
argument), and then they would be broken by implementation changes in the
future that prohibited compile-time evaluation.

~~~
mehrdadn
I see, so you're saying it's a verifiable promise by the function writer that
it can be evaluated at compile-time?

Meaning its entire purpose is to just be an easier version of a compile-time
unit-test like static_assert(f(1) == 2)?

------
reikonomusha
All of this is easy in Common Lisp, which includes interactive compile-time
debugging. You can use #., EVAL-WHEN, LOAD-TIME-VALUE, and other things to
control evaluation time, and use the same, usual, interactive debugger that
you use for runtime code, without any extra infrastructure or scaffolding.

In C++, to debug failed constexpr’s, often, you have to make it a non-
constexpr and debug at runtime.

~~~
jcelerier
or you can just use a C++ repl like Cling ?

~~~
HelloNurse
What the C++ repl does with constexpr functions would be unrelated to the code
generated by the actual C++ compiler.

~~~
marmaduke
since Cling is just Clang in a REPL, it seems like it would be good idea

------
phkahler
What about debugging? If the code to generate the data is large and complex,
how do you debug it when it only runs at compile time? Is the solution to
start with two programs like the author and then merge them? That still seems
like a maintainability issue.

~~~
dawg
In D which has CTFE support since ages, you'd debug such functions by invoking
them at runtime. [https://tour.dlang.org/tour/en/gems/compile-time-function-
ev...](https://tour.dlang.org/tour/en/gems/compile-time-function-evaluation-
ctfe) There are also tools to print values at compile time.

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.

~~~
ben-schaaf
Sadly it's fairly easy to write a function that fails at compile time but runs
fine at runtime. For those situations there's at least `pragma(msg,`

------
jordigh
Man, meanwhile, D has compile-time function evaluation. D's compiler is really
awesome. Most of the time, no special syntax is to evaluate things at compile
time (perhaps just a "static" keyword here or there) and the rules are much
simpler than C++'s.

[https://tour.dlang.org/tour/en/gems/compile-time-function-
ev...](https://tour.dlang.org/tour/en/gems/compile-time-function-evaluation-
ctfe)

~~~
dom96
If you like this you should check out Nim. Its CTFE is arguably even better.
:)

~~~
jordigh
Everyone always tells me that, but isn't Nim even more niche than D? I like
that D feels familiar and easy. I _love_ D! It's the C++ I always wanted.

------
petters
Constexpr everything can cause issues. Constexpr is a big part of a function
interface. After you have applied it, you can never add logging etc.

------
hzhou321
If you use a general meta layer preprocessor such as MyDef, you may be able to
construct macros that comes from evaluations of code in any languages (instead
of restricted in eg. C++ with rather complicated compiler mechanics)

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.

~~~
tambre
> A constexpr is a constant that is evaluated from a code at compile time,
> [...]

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.

~~~
hzhou321
With a macro, you get guarantee of compile time evaluation, as well as compile
time independent literal syntax double check. And you can do better than hint.
You can double check the results explicitly. Since it's compile time
evaluation, optimization is out of context.

~~~
tambre
If you call searching and text replacement evaluation, then sure.

~~~
hzhou321
It can be more than that.

------
DamonHD
I am already a big fan of constexpr-ing everything that doesn't run away or
make the compiler cry for our embedded application.

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.

~~~
jotux
In the last ~five years I've converted all my embedded software development to
C++11, even projects on small microcontrollers (32kb flash, 8kb RAM). There
are features I don't use but it's hard to imagine going back to C at this
point.

~~~
contrarian_
On the contrary, I've gone back to plain C for most projects and it's been a
real relief after using C++11/14 extensively. So much accidental complexity
just disappears.

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.

------
lazyjones
I'm not sure this is really necessary. gcc and Apple's clang optimize the
factorial code without "constexpr" and the fibonacci code might be an extreme
case avoided by reasonable defaults for the numerous optimization parameters
that can be adjusted:

    
    
      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.

~~~
teamhappy
You should watch the talk that's mentioned in the article. They demo a
constexpr json parser and a constexpr regex engine.

~~~
lazyjones
I tried to watch the more interesting parts, it just doesn't seem like a
useful approach to me to try to get the compiler to do this work at every
compilation instead of the traditional approach of doing it once by generating
C(++) code/structures from text/json data in my build. Especially with the
"cognitive cost" and debugging issues involved.

~~~
CamperBob2
Exactly. This technique will be high on my list of things to try the next time
I find myself complaining that my compiler is too fast.

Yet another C++ innovation that solves a problem that no one actually has.

------
plq
Genuine question: Is there anything protecting us from an entirely new class
of compiler bugs where the constexpr code and the compiled code behave
differently? Is it at all possible?

~~~
lazyjones
> Is it at all possible?

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.

------
dkrikun
I personally do not understand the buzz around constexpr etc. All the real
buziness is happening at runtime anyway and compilers already optimize code
quite decently. The extra maintenance burden just doesn't pays off.

~~~
failrate
I think you should review his premise: he is using constexpr to eliminate a
separate exe, a data file, and code that loads and reads the data file at
runtime. His maintenance burden goes down.

------
uncle_bob
C++ is never just C++. The build system for any C++ program includes both
macro language and some form of make and/or make replacement. Explicitly
generating tables at compile time and linking is bog standard at this point.

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.

~~~
Elv13
And have all embedded engineers raging because you just made cross compilation
an order of magnitude harder.

~~~
kxhost
Embedded engineer here. Care to explain why ? Code generation is widely used,
at least in automotive. Comparison of hard-to-read and hard-to-debug advanced
template/constexpr machinery vs code generated by standalone tool that is easy
to read and easy to debug would not be taken seriously

~~~
Elv13
If the generator is itself in C++, then it requires to have 2 full toolchains
to bootstrap it. Then if the developer have a great idea such as "hey, I got
access to all my code! Let's reuse it" then you have to build the package
twice. Since everytime you do that you increase the number of packages to be
built on the host toolchain, after a while they start to bleed into each other
(due to buggy build systems in the dependencies) and when you execute the
final binary, you get "wrong file format" errors on the target (or worst,
sizeof() mismatch at runtime)...

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.

~~~
kxhost
Code generators at our place are most often written in java (xtext, xtend),
rarely in other interpreted languages, almost never in C++. Of course, there
are grey areas when the price of using another tool, integrating it into build
system(which itself sometimes is non-trivial task, if done properly) has to be
carefully considered against obtained pros.

------
mehrdadn
I have yet to understand what information 'constexpr' conveys to the compiler
that makes it necessary or useful.

~~~
minipci1321
Certain things, when used in a routine, make computation impossible at compile
time. If the routine is marked with 'constexpr', the compiler will verify
that.

~~~
mehrdadn
Couldn't it already do the exact same thing without constexpr? (And _shouldn
't_ it have already done that when optimizing? In fact for simpler expressions
compilers already do this, right?) How does specifying constexpr help?

~~~
minipci1321
But I think you nailed it -- the compiler doesn't have to signal optimization
"failures" back to the developer, but it has to for the constexpr case. It is
not that the constexpr routine can be used at compile time, it is that it must
be useable at compile time.

~~~
mehrdadn
> But I think you nailed it -- the compiler doesn't have to signal
> optimization "failures" back to the developer, but it has to for the
> constexpr case.

Is this true? Where do you see Clang emit a diagnostic in the example in the
given article? ([https://godbolt.org/g/HKcPFT](https://godbolt.org/g/HKcPFT))

~~~
minipci1321
You seem to be right -- at least "no diagnostics required" is mentioned a few
times in $10.1.5 of N4700. To be honest, my comment is not from the article in
the example, but from my own experience, and that is mostly with GCC 7.2.

------
agumonkey
ad hoc partial evaluation ?

~~~
marcosdumay
With explicit purity marks.

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.

