[1] https://github.com/floooh/bookmarks
[2] https://github.com/nothings/single_file_libs (here, C/C++ usually means "written in the common subset of C and C++")
A lot of very successful and robust software is written in C. The preference for the C language is certainly not due to a personal opinion of only one person.
C is one of the most popular languages for high-performance code. Most famous example would be the Linux kernel but literally anything that needs the best optimizations possible eventually comes down to C.
That's not really true. A single compiler may support many languages but that does not mean code written in those languages ends up as the same binary.
For example, GCC's C++ front end will produce a different parse tree for the same C code as the C front end, resulting in a slightly different collection of basic blocks and data flow analysis (obviously, for very trivial examples, it will be identical). The result after all the gimplification and and different middle-end passes and rtl transformations you can end up with a surprisingly different set of generated instructions for the same code fed to gcc and g++. Equivalent, but different.
The fact is, the front ends of cc1 and cc1plus will interpret the exact same C code differently to the extent that code containing undefined behaviour will have remarkably different results in the generated instructions. The differences between the cc1 and the cc1plus compilers are not limited to what the compiler driver passes to the linker, they have completely different front ends and only share the middle and back ends.
Sure, the cc1 and cc1plus binaries come with a middle and backend linked in, but what differentiates them is the front end. Not so much for gcc and g++ where both `gcc file.cpp` and `g++ file.cpp` will invoke cc1plus. Not to mention that with LTO cc1 and cc1plus do almost exclusively front end work while everything else happens in lto1 (for both C and C++).
Good tendency however, now both C and C++ developers started experimenting with Rust, probably creating unified community and platform.
You have to use a subset of c++. Nobody agrees what that subset is. You have to be super-vigilant in code review. Features invite their use.
It's the lack of features in c that make it attractive. No magic. You want an object or a virtual, code it up if you really mean it is not just a keyword. You take responsibility for all the code running.
Every time I've cut a tonne of latency from a trading engine it's that. People relying on library and compiler without taking responsibility for it. STL is great. Except if you're actually performance critical, when you can beat it easily by solving your problem, not someone else's solution for everyone's possible similar problem.
That said, I tried to reproduce something similar, and it seems the issue only occurs in my example due to external linkage (adding 'static' fixes it)... but I can't claim this will always resolve the issue: https://gcc.godbolt.org/z/1vbqo3
But that's not the point - the point is that smart pointers never claimed to be zero-cost abstractions, AFAIK.
> I mean, who doesn't know that smart pointers can be slower than raw pointers?
Nobody... on what basis? If you're not in that set of people, that doesn't imply the set is small or empty. Googling suggests lots of people have given such advice, and, may I suggest, it's not because they were stupid.
I don't understand the question (I didn't use the word "nobody"). I stand by the first claim (library writers & ISO C++ commitee never claimed that smart pointers are zero-cost abstractions; so why would people believe that? How did that claim start?).
The second (rhetoric) question... well, of course people would occasionally believe random stuff, for God's sake, we have (non-stupid!) people who believe COVID is a conspiracy. That said - there's no basis in the belief that smart pointers are zero-cost, so it's funny to make a talk "debunking" it - to me it looks similar to a talk that "COVID is real, guys!". I was genuinely surprised this talk is needed. I think if this is really a wide-spread belief, a more insightful talk would be about how it got to be a wide-spread belief :)
So the evidence against your claim is literally within the talk.
I could understand a CPP committee member expecting a certain compiler to optimize a certain situation; however, the blanket statement that "smart pointer X is zero-cost" is more than strange, given the fact that it's always bound to be implementation-dependent, and there isn't one single C++ compiler (or even a "canonical" one) so that you could make that claim, at all. I find it really suspicious.
[edit] I watched the part of the talk - I think he was a bit surprised that has favorite compiler didn't perform that optimization, and it took him some investigation to see why. I don't think he truly expects smart pointers to be "zero-cost abstractions", I think he was just surprised that his compiler didn't optimize better that particular situation and had to dig in to find why. I still find the whole presentation a bit of a misleading stunt - there _are_ zero-cost abstractions (for some definitions of "cost" of course; and/or in some situations). E.g. in rust [1]. And even in C++ - a local unique_ptr is _probably_ a zero-cost abstraction!
BTW.. in his example - just use move semantics, put the pointer as the 5th argument, and you'll probably have the same runtime cost (ie both the raw pointer and smart pointer methods will compile to same code; since the ABI no longer allows you to pass the raw pointer via registers, it goes to the stack, so you have the additional load that bothered him in the raw pointer case, too).
[1] https://medium.com/ingeniouslysimple/rust-zero-cost-abstract...
He talks about this. C++ doesn't have destructive moves like rust does, which is the root cause of why you cannot make the unique_ptr cost zero. It'd take an ABI change to fix this. This is precisely why the scenario is interesting. A lot of people (including you) thought that "just use move semantics" would solve it.
There are indeed cases where this sort of difference in performance matters; But those are a vanishingly small group of people; and as far as those people are concerned - they typically look at assembly anyway.
He addresses this in the QA at the end. Chandler explicitly says "I still believe you should use unique_ptr and pass it by value". If somebody watches this video and takes away "don't use smart pointers" then they weren't paying attention.
It won't break reasonable code. The problem is it breaks interop with compiled binaries.
I think you're confusing my proposal with the pass-in-register proposal? Or maybe I'm missing something.
Any argument that smart pointers are not zero cost to the language because they have a cost when you use them is a classic straw man argument.
Premise one: C++ has zero overhead for most of its features, like smarts pointers: if you don't use them, they cost nothing.
Premise two: using features like smart pointers adds a cost to your C++ program.
Conclusion (due to the strawman logical fallacy): C++ is not zero overhead because there exists a feature that has a cost if you use it.
I'd like to see the reasoning that leads to the same conclusion without resorting to the strawman logical fallacy.
> I had been harboring some suspicions that our big codebases might benefit from the application of some more of the various �modern� C++ design patterns, despite seeing other large game codebases suffer under them. I have since recanted that suspicion.
I think this shows that C++ works better in controlled (i.e. corporate environments), whereas C is often preferred in Open Source.
For example, Games are heavy in C++ where there are a lot of abstractions. Engine code is mostly C style as far as I can tell. Optimized code is code that exploits specific properties of systems, hence it is significantly less abstracted than higher-level code.
The stereotypical std::sort example doesn't count, really. There are many reasons why std::sort vs qsort (which are both generic sorting implementations - i.e. not optimized ones) is not significant in practice, but it's still among the most frequently cited examples - hinting that there might be few real wins (with regards to performance) from all this compile time and templates stuff.
While you can certainly do without generics in the language, as proven by the success of many many languages, perhaps at the inconvenience of some developers, compile time meta programming is an entirely different area that only few languages really support. The high frequency trading firms and the game studios I have partnered with are typically using C++ specifically for those compile time programming features. They make dramatic performance differences in a wide range of highly specialized algorithms.
I was mostly looking at constexpr and whatever similar things have appeared in C++ lately. And I wanted to know about actual applications of them in the wild that make a difference.
Because, yeah I can precompute a 100K hash table or whatever at compile time, but I can also just do it at program startup (would anyone notice?) which is by far the simplest thing to do. Or I could just generate the data in a separate build step which is probably more hassle compared to constexpr but also probably friendlier in terms of build times in practice.
I'm not sure that this is hugely relevant these days for small stuff, though. Like < 1MB... how many instances of the same program do we have running simultaneously, anyway?.
This is just the tip of the iceberg on using templates and classes to make faster, cleaner code.
It’s also hard to generalize these things in the form of those kinds of macros. Whereas with something like Eigen, just write your code like normal, you don’t have to worry about the special cases, and the compiler rewrites it for you. That’s one of the nice benefits, one of many, of metaprogramming.
Consider the simple problem of multiplying together a sequence of N matrices of possibly different sizes with the least amount of work. The order you multiply in is determined via some optimization technique. You can try to have a different C function for each N, but eventually you will have some N for which your lib doesn't have the call. Or maybe you'll try to pack pointers into an array and pass that, which is now slower and more memory costly. In any case the order must be solved at runtime.
Templates allow, at compile time for known size matrices, the order to be determined. This cannot be done in generality with C since you cannot in C do it.
And, if the matrices were constexpr, this can be computed at compile time.
So the template method, giving you Turing complete operations, can do things that you cannot do in C.
This is just a simple example, the tip of the iceberg.
Eigen is a great library and very convenient to use. It's great to be able to write straight forward chains of matrix operations and trust that the resulting program will be reasonably fast. There's no need to be dogmatic about C vs. C++, though. They're both higher level languages targeting the same underlying hardware. Templates enable library developers to make simple APIs at the expense of more complicated library implementations. In C, it's often necessary to compromise on the simplicity of the API to achieve the same performance, but it also generally means that the library implementations are simpler. The overall quality of the resulting binary can be about the same, and is almost certainly within the same ballpark performance wise. As an embedded engineer, I often need APIs that are compatible with C whether or not the implementation is C++, and I value simple library implementations over complex ones; the libraries and my use cases are often obscure enough that they are buggy, and so the more readable the library is, the easier it is for me to debug them.
As a recent real world example, a coworker, who is a wizard that knows way more than I do about signal processing, implemented some matrix heavy algorithms in a high level language that supports just-in-time compilation down to parallelized CPU and even GPU machine code. It worked great on an x86-64 workstation, but on production hardware, we struggled to get the code to run fast enough; it would peg all the CPUs at 100%. The many layers of libraries and JIT compiliation made the system very hard to debug even after a couple weeks of trying. I suggested re-implementing the algorithm in C++ using whatever matrix library was most convenient, and a few days later the system was running perfectly and averaging 14% of one CPU. The algorithm went from maybe 50 lines of very readable code to 250 lines of relatively ugly code, but we understood what it was doing way better. I believe he used Eigen in the C++ implementation, but whether or not the matrix library was optimized at all, C, C++, or rust, it still would have sipped around 14% of one CPU. My point is that, when performance matters, you need to understand what the software and hardware is doing, and so there's value in simplicity and pragmatism.
But you were talking about generics, which implied you were incorrectly conflating that with template meta-programming, since generics are done with templates.
> I don't think of them as a practical way to do compile time computation
Templates are however the most flexible, advance technique for C++ compile-time programming, though the C++ standard is evolving to bring more and more of those features into the language without using templates, i.e. "constexpr if".
My recommendation would be to avoid speculating on what the benefits are of a language or its features if you clearly don't have serious experience using them. It's fine if C++ is not for you.
I'm aware this is a pointless discussion, but please double check your claims are right if we are in "check mode". I did not say "generics".
>> Any examples of using compile time features that make a difference, instead of making code harder to maintian and increasing compile times significantly?
> My recommendation would be to avoid speculating on what the benefits are of a language or its features if you clearly don't have serious experience using them. It's fine if C++ is not for you.
Maybe you shouldn't make such statements if you don't know about my experience. I am speaking from experience, and exchanging subjective experiences isn't worthwhile most of the time, but sometimes (if people don't go down to personal attacks) there is a new viewpoint to find.
FYI if you’re talking about compile-time programming and you use the word “generic“ (as you did, in the context of a generic sort, which actually does have a meaning related to generics, as the routine works on containers of any type), note that this is a well-established term, which could be confusing if you actually are referring to something else.
I'm yet to see a 'modern web browser' that is fully written and only written in C. (C bindings don't count)
when they went to c++ it became Firefox. do you think we'd have a better firefox today had it stayed in C ?
Firefox came a long time later. Well after Mozilla opened.
No idea if Firefox would be better if they'd stayed in c. What i do know is that c++ is a vastly, vastly better language now through no fault of the standards committee. G++ got good. Then came clang++, valgrind was an amazing leap forward too.
Before all that there really was no such thing as cross platform c++.
that's a pretty harsh take on it - how would you design it considering that initialization order has to be respected (else a lot of use cases would break; it's definitely not uncommon to have a member of a struct to depend on a previous member being initialized) ?
I have yet to find a particularly blocking use of the feature when test-driving it - it's on the other hand a great boon for readability (and definitely puts a nail in the coffin of named function parameters proposals imho).
Clang has demonstrated long before C++20 that it is possible to use the full C99 designated-init feature set in C++, so I really don't understand why the C++ Committee had to cut down the feature when there was already a fully working implementation.
PS: as an example for where C++20 designated initialization breaks down, see for instance [1], I tried to replicate this code in C++20, but quickly gave up (with the main problem being that it is impossible to remember the correct declaration order in big "option bag" structs like this.
[1] https://github.com/floooh/sokol-samples/blob/e0a93a21b17a663...
Another big one is implicit casting of void ptrs. I cast void pointers to specific types A LOT. There are many situations where you have to cast void pointers (that come from an abstract API that only deals with untyped memory) to specific types (higher-level application code), and I can't really be sure, but I think there isn't a nice way to do that in C++.
It might be because C++ wants you to use their elaborate abstractions and templates, which in many situations is just fluff that makes programs harder to read and maintain.
C++ also supports C-style casts but it's a code smell.
Not sure if this is impossible in C++, I think some of the alignment functionality added in the past several versions might cover this, but it is not as simple.
IMHO, Not a big reason since every compiler offers some non-standard keywords to cover this use case, but a reason nonetheless