Constexpr function evaluation sounds like a great idea until you start trying to use it, and get surprised when seemingly-constexpr-safe functions aren't constexpr. Or, you tweak one function and suddenly all your fancy compile-time unit tests explode.
Ok, so you get around that with good code hygiene and by limiting the complexity of your constexpr functions... in other words, do the exact things we already do with preprocessor macros.
Alternatively, you add a constexpr keyword to the lang, but now we have red functions and blue functions. Great.
In another language, there'd still be an argument for the type-safety that would precipitate from constexpr function eval, but this is C we're talking about.
How about container_of? Could we please standardize that already? Why is this crucial and immensely useful macro a thing we all copy-paste from that one page on kernel.org?
Just like constant expressions, a function run at compile time needs to be pure, which means no globals, no system calls, no I/O, no undefined behavior. Yes, that does constrain it somewhat, but D users have found it to be immensely useful anyway.
There's really no comparison with preprocessor macros. All the preprocessor can do is trivial expressions with long values. Not even floating point.
> you add a constexpr keyword to the lang, but now we have red functions and blue functions
My proposal (and D) does not require constexpr. The same function can be used at run or compile time. There is no need for that keyword. C++ made a mistake.
> D users have found it to be immensely useful anyway.
I think you're focused on something that D programmers found helpful, rather than focusing squarely on the needs of C programmers. C and D are both good languages. Their use cases can overlap, but frequently don't.
>=70% of the code I write for work is C, as I'm an embedded firmware dev. C meets the very particular needs of bare-metal development, a use case that continues to be underserved by Rust, Zig, and other supposed successors to C. So, I'll be approaching this with a strong bias towards that perspective.
To me, the argument about unit testing rings hollow because of all the other, far more complicated and runtime-subverting things that would also need to become standardized before it would be feasible to unit test C programs without help from the hideous hacks we use today, like CMock. So, all of that isn't doing anything for me.
> My proposal (and D) does not require constexpr. The same function can be used at run or compile time.
Like you, I dislike the constexpr keyword. But, particularly considering your own example of defining an enum value, I don't see how the "implicit const-evaluatable" approach makes my life easier. Calling functions to define an enum's value is cute, but I can't tell you why I'd actually want to do that. You mention that the preprocessor can't handle floats, but, well, neither can enums!
Adding a single printf (or, in my case, kprintf or LOG_DBG or whatever) would become liable to nuke some constant evaluation happening somewhere far up an obscure call chain. The basic reality of C is that you will, at one point or another, encounter a situation where your only debugging tool is print statements (or a single LED). That's the cold reality of C's paper-thin runtime.
So, I really dislike the idea of having a feature that's liable to make your code go "boom" at compile time because you put a print statement in just the wrong spot. Or a write to a memory-mapped register. Or a hard jump into a blob sitting somewhere in memory. Or inline assembly. Or a call to a function that uses any of those things, even once. Even without the keyword, you end up with red functions and blue functions. It's just harder to tell which ones are which.
Defining some const floats? Sure, that's neat. If you could use this feature to define huge matrices of floats, that'd be pretty cool! There's just a boatload of gotchas.
That is hardly the only place that has a constant-expression in the grammar. (BTW, D enums can also be floats, and even string literals!) You could use CTFE to initialize const floating point globals. static_assert also takes a constant-expression.
> There's just a boatload of gotchas.
The D community has 17 years experience with it. It remains an indispensable feature.
As for embedding a printf, that has come up. Recall elsewhere I said that only the path through a function has to be pure for CTFE to work, not the whole function?
int sum(int a, int b) {
int s = a + b;
if (!__ctfe) printf("sum is %d\n", s);
return s;
}
__ctfe is a keyword that says "CTFE is executing this function".
> If you could use this feature to define huge matrices of floats, that'd be pretty cool!
I use it to statically initialize complicated tables at compile time. Before CTFE, I wrote a separate executable that would emit source code with the array initializer. I like the new way mucho bettero.
If you prefer "hideous hacks" (your words!) I won't take that away from you.
Not to take away from the possible usefulness of constant functions, but it's amusing that the article example of `sum(5,6)` would work great in C if written as 5 + 6, or done as the equivalent macro
Trivial examples make it easy to explain and understand the concept. Just like with Calculus, we don't start with integrating e^x. We start with integrating x.
Constexpr function evaluation sounds like a great idea until you start trying to use it, and get surprised when seemingly-constexpr-safe functions aren't constexpr. Or, you tweak one function and suddenly all your fancy compile-time unit tests explode.
Ok, so you get around that with good code hygiene and by limiting the complexity of your constexpr functions... in other words, do the exact things we already do with preprocessor macros.
Alternatively, you add a constexpr keyword to the lang, but now we have red functions and blue functions. Great.
In another language, there'd still be an argument for the type-safety that would precipitate from constexpr function eval, but this is C we're talking about.
How about container_of? Could we please standardize that already? Why is this crucial and immensely useful macro a thing we all copy-paste from that one page on kernel.org?