> It is always a matter how good are those benchmarks written.
I agree it's not perfect, but I'm yet to see a single set of benchmarks show that "C++ effectively passed C a while ago" and yet I see it claimed somewhat frequently around here. Generally the games I see around benchmarks go the other way though, with people effectively writing C in the comparison language and not idiomatic code.
> For example, good luck achieving in C what is possible to do in C++ with constexpr at compile time
The only thing constexpr solves AFAIK is doing it all in c++, generating compile time constants is hardly magic and it's been solvable with anything from a shell script to something more complicated forever.
> Or sorting data via qsort() versus STL sorting algoritms with templates, which in C always require the runtime cost of an additional indirect call.
It's not as ergonomic, but things like this are also doable with _Generic in modern C.
Then look at Eigen vs alternative C libraries for example.
Constexpr allows for compile time code execution, introduced in C++11 and extended with each C++ revision, C++20 version even allows for partial STL use.
There is plenty of stuff one can do with it, when coupled with template metaprogramming, it is basically C++'s version of Lisp macros, although a bit clusmy.
Generating constants is just the tip of the iceberg.
Using shell scripts is language agnostic solution.
_Generic is very constrained, try to implement std::sort() for any kind of datastructure with it, not only basic numerical types.
On the other hand, there is exactly one copy of qsort in your entire program image, no matter how many different uses it is applied to. That's better from a caching POV than numerous expansions of templated sorting.
Caching is everything; something that does an indirect call, but through a hot cache, is usually better than something that avoids it, but thrashes the cache.
Caching is so important that byte code that fits into a L1 cache (together with the byte code dispatcher) can beat native code that doesn't fit.
Indirect calling itself isn't so bad when it isn't computed. The comparison function being called by qsort stays the same over the entire job. The source operand which provides the call address is known way in advance of the indirect call, making it possible to pipeline through that without stalls.
> good luck achieving in C what is possible to do in C++ with constexpr at compile time
You can always just type out all the code you want by hand, including all the cases, using a liberal sprinkling of copy and paste. You can open-code some quicksort by hand, or else piece it together out of some prolog/payload/epilog type macro fragments or whatever.
It's just not generic and reusable that way; but in many C programs, that doesn't matter at all.
At the end of the day, we are somehow still using LAPACK routines written in Fortran for number crunching.
constexpr is much more powerful than just generating constants (although that is one of the bigger use cases). One of the things I like is how it simplifies doing static dispatch on template types, essentially using SFINAE under the hood but in a much simpler way to read:
if constexpr ( std::is_same_v<T, int> ) { ... }
Vs
template <typename T, typename std::enable_if_t<...>* = 0>
void fn( T t ) { }
Being able to generate constants using the language itself is super powerful as well (the below is a bit contrived, as std::string_view is constexpr and obsoletes this pattern):
static constexpr std::array<char[], SIZE> strs = { ... };
static constexpr std::array<size_t, SIZE> str_lens = calc( strs );
// Where calc() loops over the array and calls std::strlen, all at compile time
The logic is more "why add complicated features to a language when there are existing ways to have the same functionality". C++ is already over complicated, the last thing it needs are more features. This effort is also being replicated across several languages with all there own syntax and warts. Generating source code via a script or template library is much more compositional.
I'm not sure if adding an additional language or two to C makes the codebase simpler than pure C++ code.
What's more, I've witnessed quite a few times the code generating program becoming incomprehensible after more and more parts of the code gets generated conditionally.
If you're using macros or another language entirely to generate C code, it's often to preserve type safety. Otherwise it's often easier to typecast everything through void pointers.
It's the same situation with Go, so it's not like C is entirely anachronistic with regards to the trade-offs it makes.
Macros and type safety don't really go hand in hand, hence why ISO C++ is progressively removing any reason to still use them beyond conditional compilation.
Go, just like C, is stuck on a by gone era of programming languages.
I agree it's not perfect, but I'm yet to see a single set of benchmarks show that "C++ effectively passed C a while ago" and yet I see it claimed somewhat frequently around here. Generally the games I see around benchmarks go the other way though, with people effectively writing C in the comparison language and not idiomatic code.
> For example, good luck achieving in C what is possible to do in C++ with constexpr at compile time
The only thing constexpr solves AFAIK is doing it all in c++, generating compile time constants is hardly magic and it's been solvable with anything from a shell script to something more complicated forever.
> Or sorting data via qsort() versus STL sorting algoritms with templates, which in C always require the runtime cost of an additional indirect call.
It's not as ergonomic, but things like this are also doable with _Generic in modern C.