10 ms·
> C++ has supported compile-time evaluation of first-class functions for over ten years, while C is still limited to using second-class language features in com
by dataflow 5y ago
> C++ has supported compile-time evaluation of first-class functions for over ten years, while C is
still limited to using second-class language features in compile-time contexts
It's funny that of all the gazillion features C++ has supported a for well over a decade the C folks have been missing out on, they pick exactly the one superfluous keyword in C++ to cherrypick onto C. You have to wonder what will happen if/when C++ removes the requirement for the keyword later.
- AlexanderDhoore 5y agoDo you mean that C++ could have done without `constexpr` and could have used the existing `const`? Because that has been my feeling for a very long time. Why not expand the meaning of const in certain contexts. Compilers know if something can be evaluated at compile-time. No need for me to tell them explicitly.
- zarzavat 5y agoIt's not the same. e.g. const int x = readInput(); cannot be constexpr. Consider constexpr as a compile time assertion that a given symbol is available at compile time. If you didn't have the constexpr keyword then you would have have to inspect the definition to determine that every subexpression is constexpr.
- AlexanderDhoore 5y ago"inspect the definition" Yes, that's what I meant with "the compiler can do it".
- zabzonk 5y agoNot given C and C++ separate file compilation.
- zarzavat 5y agoThat might be OK in an IDE driven language such as Java, but I doubt the neckbeards writing C in a plain text editor would go for that level of implicitness. In C++ it might be more acceptable since you practically need an IDE since C++11 to resolve all the auto type inference.
- foxfluff 5y agoC neckbeard here. I do fully hope and expect and assume the compiler to fold constants and figure out as much at compile time as it can. This is the kind of optimization that doesn't make the code harder to reason about, even when coding with ed, since the logic and ultimate result of these expressions does not change. Heavy type inference, overloading, and inheritance are a completely different beast and they make the code a lot more opaque.
- deleted 5y ago[deleted]
- nine_k 5y agoWhy not const int x = const foo(bar(y)); ?
- mpyne 5y agoThe compile can (and will) do that at run time, it just won't let the code alter x afterwards (either at compile time or run time)
- SuchAnonMuchWow 5y agoYou should check Zig comptime keyword, which does exactly that. Its the feature from zig I miss the most in C.
- User23 5y agoThat sounds like a subset of EVAL-WHEN[1]. [1] http://clhs.lisp.se/Body/s_eval_w.htm http://clhs.lisp.se/Body/s_eval_w.htm
- pjmlp 5y agoYes, all the constsomething is superfluous, as proven by Circle. https://www.circle-lang.org/ https://www.circle-lang.org/ Unfortunately, there is a big political war ongoing, so instead C++ keeps collecting const.... keywords.
- voldacar 5y ago"Constexpr" is possibly the most hideous keyword in any programming language. My eyes vomit a little each time I read it. They could have just used "pure" like in D, but this is C++, so that would be too sane and obvious.
- pjmlp 5y agoPolitics, check the outcome of the networking vote.
- hmfrh 5y agoIntroducing a new keyword means that all existing code that uses the new keyword as an identifier will break. That's why the `constexpr` keyword is deliberately weird. It's also why they reuse keywords like `using`.
- kevin_thibedeau 5y agoThey could just use the reserved keyword naming like the C committee does.
- nly 5y agoI can't confirm in this specific case but, more often than not, the reason for these redundancies is a fraction of the committee felt uncomfortable with the implicit solution, so the keyword got added.
- pjmlp 5y agoThere is a video about the story background, where a group of kids play the role of the committee voting down Circle.
- dataflow 5y agoBasically, yeah. You don't even need 'const' for this. Under the as-if rule, subsets of the program could always already be evaluated at compile time and have the result substituted in the program. Compilers already did that when they had the definition handy. Like just do int x = f(5); and then define int f(int x) { return x * 2; } and you'll get int x = 10; in any compiler without any keywords whatsoever. The constexpr keyword just cluttered the code for no good reason.
- NavinF 5y agoIn theory compilers can always optimize constant expressions, but they don't IRL. See my example above where I multiply the elements of an array containing {2,2}: https://news.ycombinator.com/item?id=28886999 https://news.ycombinator.com/item?id=28886999 My code is C style and unrealistically simple, yet the compiler can't even constant-fold that. If you've looked at the output of real compilers, you'd know that it's unrealistic for a language to let you to use the result of an arbitrary function call where you'd normally use a constant.
- dataflow 5y agoI'd already replied to you on that example and explained this has nothing to do with optimizations as far as performance and mechanics go. The only relevance of optimizations is to show that the language semantics always permitted it. Not to suggest an optimize needs to be run to actually do this.
- comex 5y agoIt depends. For one thing, constexpr guarantees that errors will produce compilation failures. (At least it guarantees that when used in certain ways; unfortunately C++ overcomplicates things.) In a version of C that doesn't have an explicit constexpr keyword but does allow the compiler to treat arbitrary variables as constants, you could perhaps approximate this by using a value in a context that forces it to be a constant expression… e.g. `const int x = foo(); _Static_assert(x == x);`. But that's a hack. Much better to have an explicit way to say "please evaluate this at compile time". Also, suppose the user doesn't use an expression in a way that forces it to be evaluated at compile time, but you still want to allow the compiler to do constant evaluation for the sake of faster runtime performance or smaller code size. In this example, should fib(35) be evaluated at compile time or runtime? unsigned int fib(int n) { return n <= 1 ? 1u : fib(n - 1) + fib(n - 2); } int main() { fib(26); } It depends on the user's intent. In this case, evaluating fib(35) takes less than a millisecond at runtime on my computer, but evaluating it as constexpr with clang requires about a full second. (After all, naive fib performs an exponential number of recursive calls.) Sounds like it's probably a job for runtime… unless the user really values runtime performance or code size more than compile times (maybe they're targeting a really slow CPU), in which case they might prefer it to be done at compile time. Best to give them a choice. Making things worse, the compiler has no real way of knowing how long something will take to constant-evaluate other than trying and seeing. It can give up after a timeout, but if the timeout isn't very tiny, it risks wasting a lot of time on aborted constant evaluation attempts. Therefore, while optimizers already try to perform opportunistic constant folding, they have to be very conservative with it. This problem doesn't occur if the compiler knows that something should be constant-evaluated because the user has marked it as such.
- secondcoming 5y agoIs it superfluous? I will admit that when I first read about constexpr my first thought was 'why can't the compiler figure this out automatically?'. I assume that compiler vendors all agreed that a new keyword was necessary, they don't add them without reason.
- rowanG077 5y agoThe compiler can figure it out. But that doesn't solve the problem constexpr was created for. Without constexpr compile time evaluation becomes very brittle. Constexpr is there to help the compiler and developer reach an understanding of the stuff that has to be evaluated at compile time.
- atq2119 5y agoThe problem is that (especially library) code just becomes absolutely littered with constexpr, because realistically speaking, you do want everything that can be constexpr to actually be constexpr.
- rowanG077 5y agoWhy is that a problem? I actually really like seeing if a library function can be cte'd.
- dataflow 5y ago> Why is that a problem? Because whether a call should be CTE'd is a decision for the caller to make, not the callee. > I actually really like seeing if a library function can be cte'd. That's not what you're seeing with constexpr. It's what you're seeing with consteval. [1] What you're seeing with constexpr is which functions are prohibited from being CTE'd. Which is pretty useless when you don't intend to CTE them in the first place, and actively counterproductive when you do. [1] Actually this is kind of a lie too [2], but at least it's much closer to the truth. [2] Notice that seeing consteval here tells you absolutely nothing: https://gcc.godbolt.org/z/bWzd4v6Y1 https://gcc.godbolt.org/z/bWzd4v6Y1
- nuerow 5y ago> It's funny that of all the gazillion features C++ has supported a for well over a decade the C folks have been missing out on, they pick exactly the one superfluous keyword in C++ to cherrypick onto C. It looks to me that it's a significant improvement that's also a very low hanging fruit, given that C/C++ compilers that already support C++'s constexpr can trivially support it for C as well.
- saagarjha 5y agoNot every C compiler is a C++ compiler.
- NavinF 5y agoBesides that, I suspect nobody in this thread has looked at what compilers output. Compare the following programs that multiply 2*2: https://godbolt.org/z/avsac77aa https://godbolt.org/z/avsac77aa https://godbolt.org/z/Mznhrf1sq https://godbolt.org/z/Mznhrf1sq The only difference between them is that `len` and `result` are declared constexpr in the first program. Notice how without constexpr, both clang and gcc generate a slow call to mul() inside main() instead of just calculating 2*2 at compile time. I'm sure you could use LLVM opt to force to compiler to try harder than -O3 (and maybe that should be the default), but that will also make compile time longer. Real code is way harder to optimize than my 18 lines that are all in 1 file.
- deleted 5y ago[deleted]
- dataflow 5y ago> I'm sure you could use LLVM opt to force to compiler to try harder than -O3 (and maybe that should be the default), but that will also make compile time longer. Real code is way harder to optimize than my 18 lines that are all in 1 file. Evaluating code at compile time is not harder or even as much work as than optimizing it. If you ever wrote an interpreter you know that it's much simpler than optimized codegen. You can evaluate unoptimized code at compile time perfectly fine, and deducing whether that is possible is extremely fast in comparison. Which is why in C++ you see people litter constexpr everywhere they can regardless of whether the code is -O0 or -O3.
- jcelerier 5y ago> You have to wonder what will happen if/when C++ removes the requirement for the keyword later. I don't think it's that simple. You need a way to differentiate code that runs at compile time and code that runs at runtime (because at compile time you can afford a "slow" algorithm, while at run-time you'll be calling into a proprietary binary which will do the computation on a GPU or something like that).
- dataflow 5y ago> I don't think it's that simple. You need a way to differentiate code that runs at compile time and code that runs at runtime (because at compile time you can afford a "slow" algorithm, while at run-time you'll be calling into a proprietary binary which will do the computation on a GPU or something like that). You mean std::is_constant_evaluated()? You don't need constexpr to be able to have that.
- jcelerier 5y agobut how will you differentiate a call that you want to perform at compile-time vs at run-time? e.g. if you want to run some unit tests to check the two algos, today you'd do: int res = my_algo(); CHECK(res == 123); ... constexpr int res = my_algo(); CHECK(res == 123); // compile-time version is the same with int my_algo() { if constexpr(is_constant_evaluated()) { return 123; else return some_lib->very_advanced_computation(); } how do you do the same without the constexpr keyword ? i'd rather not introduce a template struct just to get a constexpr context, à la template<auto Value> struct manually_constexpr { static constexpr auto value = Value; }; CHECK(manually_constexpr<my_algo()>::value == 123);
- petters 5y ago"constexpr" does not do what you think it does. For example, "if constexpr(is_constant_evaluated())" is always true, I think. See also the new C++ keyword "consteval" (yes, really!)
- marcodiego 5y agoI think you're completely ignoring more radical proposed changes like lambdas: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2738.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2738.pdf and more modern error handling: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2361.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2361.pdf Also consider that proposals must satisfy http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2021.htm http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2021.htm small list of relevant points: 4. Avoid “quiet changes.” 6. Keep the spirit of C. 9. Minimize incompatibilities with C90 (ISO/IEC 9899:1990). 11. Maintain conceptual simplicity.
- dataflow 5y agoI'm not ignoring anything, I just mentioned C++ has had a ton of features over C, some well over a decade (and some just about and some newer). Moreover I can't even see how I could've mentioned lambdas. They have a superfluous keyword? Which one?
- dataflow 5y agoEdit: Whoops, just had a chance to read these comments more carefully. I didn't realize you're talking about C rather than C++. I didn't ignore them; I just wasn't aware of those proposals. (Certainly I haven't seen them on HN, and I don't keep up with all the proposals.)
- huhtenberg 5y agoNot sure if you were trying to make this point, but as others have noted constexpr almost certainly violates #11 (by mixing run-time and compile-time variables) and through that it also violates #6.
- bonzini 5y agoThe two things I would add to C are RAII (like GCC __attribute__((cleanup)) and glib's g_autoptr wrappers), and variant types (mostly so that we could have an equivalent of Rust's Result type).
- boulos 5y ago... constexpr isn't superfluous. The compiler can/could/does absolutely infer that a thing is a compile time constant and do it for you. But, if you declare a thing constexpr and someone updates some part of it to be !constexpr, you get a nice compiler error. Without labeling the value as constexpr, you don't get that certainty. Edit: is the argument that const static is also that? (If so, only for direct initialization of structures / you still can't have the equivalent of a constexpr function).
- dataflow 5y ago> But, if you declare a thing constexpr and someone updates some part of it to be !constexpr, you get a nice compiler error. Without labeling the value as constexpr, you don't get that certainty. That's not really true. You don't get that "certainty" with constexpr either: template<class T> T f(T); template<class T> constexpr T g(T x) { return f(x); } Notice you won't even get a warning with -Weverything under Clang! > Edit: is the argument that const static is also that? (If so, only for direct initialization of structures / you still can't have the equivalent of a constexpr function). For variables, const static should probably evaluate at compile time whenever possible, like you mentioned. For functions, constexpr is really useless (again, see above as just one example) and an inline declaration serves pretty much the same practical purpose. I think you're thinking of 'consteval', which is at least not completely useless, so it's a little easier to justify. Even then, it should've probably been an attribute rather than a keyword.
- boulos 5y agoHmm, is that just because of the template resolution? (and so wouldn't apply to constexpr in C) AFAICT, something about the template declarations is making the call of f(x) to be "who could know" (which should just be fail). The same thing with concrete types says that f isn't constexpr [3]. And you have to make it actually constexpr by removing the static from counter [4]. [1] https://docs.microsoft.com/en-us/cpp/cpp/constexpr-cpp?view=msvc-160 https://docs.microsoft.com/en-us/cpp/cpp/constexpr-cpp?view=... [2] https://godbolt.org/z/7rGWbP1xz https://godbolt.org/z/7rGWbP1xz [3] https://godbolt.org/z/5WjMr3Kjv https://godbolt.org/z/5WjMr3Kjv [4] https://godbolt.org/z/v9rc1j9zc https://godbolt.org/z/v9rc1j9zc