5 ms·
`if constexpr` is such a disaster. They were so close to getting it right (not introducing a scope) but they missed. Similarly constexpr itself is also genuine
by mhh__ 3y ago
`if constexpr` is such a disaster. They were so close to getting it right (not introducing a scope) but they missed.
Similarly constexpr itself is also genuinely ridiculous: (I have said this on hackernews before) It's such a stupid idea to require an annotation everywhere you want to evaluate things at compile time, practically everything will inevitably be evaluatable at compile time, and you need the implementation anyway, so just let it fail rather than ask for permission everywhere.
Having the keyword for variables and constants is fine (i.e. top down and bottom up constraints need to be dictated) but you shouldn't need to write constexpr more than that.
- Espressosaurus 3y agoconstexpr is great. I can finally do most of the compile time computation that previously required template metaprogramming, and it's much more readable by comparison. C++17 makes it much more ergonomic to use too over C++11. I can't wait for us to finally upgrade our toolchain to take advantage of C++20. If you're in embedded and you're not pushing everything you can into constexpr, you're missing out on correctness and code size benefits.
- mhh__ 3y agoDoing stuff at compile time is good (although the compiler can do 99% of it anyway its nice to have guarantees), C++ just got it wrong.
- Espressosaurus 3y agoIt could certainly be better, but in a world where my options are C, C++, or assembly, I pick C++ every day. I do keep hoping for something else to get big enough we can use it because C++ sucks, but is still better than the alternatives we can use.
- mhh__ 3y agoC++ is much better than C. Personally I use D everywhere I would've previously used C++.
- titzer 3y agoVirgil doesn't have all the backends for the diversity of embedded ISAs out there, but the first version compiled to C and ran in as little as dozens of bytes of memory (on 8-bit AVR). Nowadays I am not doing embedded systems, otherwise I'd write more backends.
- moron4hire 3y agoThe example in the article is a little weird, too. Putting aside the weirdness of the intended purpose of the example, what's wrong with function overloading? I think the overloaded version is a lot easier to read. With the `if constexpr` version, now I have to edit the one function with new cases if I add a type that doesn't fit the "number or string" pattern, whereas with overloading I just implement a new `length` function.
- mhh__ 3y agoThe example is weak but a good thing to look into is https://dconf.org/2017/talks/alexandrescu.pdf https://dconf.org/2017/talks/alexandrescu.pdf i.e. this type of compile time branching (when implemented properly as mentioned above) lets you write code that "reacts" to other code in the project's capabilities. Allows some very nice patterns within a C++ flavoured type system.
- gumby 3y agoI wish constexpr actually asked the compiler to evaluate an expression at compile time and not just a statement.
- WalterBright 3y agoIn D, every part of the grammar that is a constant-expression must be compiled at compile time. For example: void test() { int square(int y) { return y * y; } int x = square(3); // evaluate at square at run time enum y = square(3); // evaluate at square compile time } (Although, when the optimizer gets through inlining square(), etc., it will wind up at compile time anyway.)
- masklinn 3y ago> It's such a stupid idea to require an annotation everywhere you want to evaluate things at compile time, practically everything will inevitably be evaluatable at compile time And making constexpr-ness an explicit contract makes sense to me: if it's not that it can be an unexpected property, and can break at any change of implementation. Yes requiring a function being marked requires that the implementor do it, but it also means they have considered this use-case and made it officially part of the API. It's not a trivial promise to make. > and you need the implementation anyway, so just let it fail rather than ask for permission everywhere. "Let it fail" is the issue, if it's implicit a user can assume this is working by design, then find their program stops compiling on the next release not because the maintainer wilfully broke the API, but because they changed an implementation detail of the function and constexpr-ness is not something they considered (or assumed correct) in the first place. Maybe the language should work the other way around and everything should be constexpr by default and functions should opt out of constexpr-ness, but that's 40 years too late for C++. And I can't think of any langage which does that. And frankly it feels like the wrong default for the reasons above.
- Jasper_ 3y agoExcept constexpr isn't a guarantee. Compilers can choose to silently evaluate constexpr things at runtime. And I have ran into this before: a compiler with a recursion limit causing it to bail on constexpr and just emit the code. So constexpr isn't a guarantee of it being evaluated at compile time, and non-constexpr isn't a guarantee of it being evaluated at runtime. Cool, huh?
- comex 3y agoC++20 has consteval and constinit to fix this (at the cost of making things even more complicated).
- MrBuddyCasino 3y agoAmazing.
- 3y ago
- jenadine 3y agoDisaster? Just because it introduces a scope? Please elaborate. A scope is the right thing to do, every other {} introduce a scope, so if this wouldn't have been consistent with the normal 'if' that would have been extra confusing. Maybe you'd like to do something like: template<typename T> struct ABC { int x; int y; if constexpr (is_3d<T>) { int z; } }; But yeah, that's not what if constexpr does.
- jcelerier 3y ago> `if constexpr` is such a disaster. to me it's been a very useful tool for reflection, for instance if constexpr (requires { foo.someMember; }) { use(foo.hasSomeMember); } else { some_fallback_case(); }
- mhh__ 3y agoThis is "design by introspection". It works a lot better if you can 1. Do this in types. 2. Do this without introducing a new scope inside a function.
- FpUser 3y agoI would absolutely hate `constexpr` to be implicit and left to be decided by compiler.
- thebigwinning 3y agoWhat if it was always allowed to do compile time, but you could add a keyword to assert it did (like force-inline). That way you get benefits by default. Whereas right now it's not allowed to.
- masklinn 3y agoThese are two completely different things. One is the provider of the API promising that the function supports compile-time evaluation. The second is the consumer of the API ensuring that an expression has been compile-time evaluated. It's nice that the consumer can make sure, but that's not helpful if the provider of the API never specifically intended for a function to be evaluatable at compile-time. > Whereas right now it's not allowed to. Of course it's allowed, but you're at the mercy of the compiler's decisions.
- thebigwinning 3y ago> Of course it's allowed, It's not. If you to use a function in a constexpr context then it will complain it needs to be marked as such. So the API creator needs to label every function in case a client wants to use it.
- mhh__ 3y agoIt is decided by the compiler. Note that I mean at the declaration not the callsite.
- WalterBright 3y agoHow D decides it is if the grammar says its a constant-expression, whatever makes up the constant-expression gets evaluated at compile time - functions and all. For example, int square(int x) { return x * x; } static assert(square(3) == 9); This allows one to write unit tests that are checked at compile time, whereas: int main() { assert(square(3) == 9); } is always a runtime check. Compile time unit testing is a significant win: 1. it's always more productive to find problems at compile time rather than run time 2. it isn't necessary to conditionally turn off compilation of the tests for the release build 3. you can't forget to run the tests before shipping
- titzer 3y agoThey should just skip to the end and make the entire language legal to execute at compile-time. Virgil does this and many Lisps before it. You then get a heap against which the entire program can be optimized at compile time. C++ is slowly catching up to Virgil I for microcontrollers, ala the 2006 paper.
- scharliee 3y ago>Having the keyword for variables and constants is fine (i.e. top down and bottom up constraints need to be dictated) but you shouldn't need to write constexpr more than that. This. Constexpr should have been at the eval site, i.e something like: consteval auto x = foo(); And foo() is just a normal function, if the compiler can eval it at compile time - all good - if not, compiler error.
- UncleMeat 3y agoThe reason why you want it to be part of the API contract is to avoid future breaks. If you publish a library as "constexpr" you are indicating to your users that they can use it in a compile-time context and that future changes to your implementation will remain compile-time executable. If you just say "anything that can be computed at compile time gets auto-deduced to constexpr" then you rely on some library that is compile-time executable by coincidence but you really really really need it to be compile-time executable. Now when that library owner makes an edit that means it cannot be executed at runtime your code breaks.