C++ Initialization Story
old.reddit.com
old.reddit.com
So what I want to say is, it's not necessary at all to understand the language completely or even mostly in order to do good, high quality, productive work with it. Not even close.
Maybe with the advent of AI tools like co-pilot I can even start to forget more ;)
I used to code in C++ with Borland C++ Builder and it felt almost the same like doing WinForms with C# pracrtically but once you want to really understand the language and what's going on the deeper level C++ seems infinitely obscure.
No. There's no comparison. Assembly is far harder.
Assembly can be completely unstructured. It can jump into the middle of a subroutine. OS permitting, it can be self-modifying. You have to have a very clear understanding of how each instruction can affect the condition flags. Instead of getting very familiar with the C++ spec, you're getting very familiar with the chip spec.
You can write clean, understandable assembler. To do so, you have to have self-discipline. You have to resist using all the things that the assembler makes possible. But you can do that in C++, too - and should.
There's no IFNDR in assembly. (Ill-Formed No Diagnostic Required: the C++ get out which says well, this isn't a C++ program and so it has no defined meaning but your compiler probably won't even warn you about that, so your code does compile it just has no defined behaviour)
I've often thought that. If nothing else, if a given opcode (say, an ADD) is being used, things like integer wraparound are going to do something specific and well defined, not cause undefined behavior and invalidate the entire execution of your program. I miss that from my 6502 assembly programming days :-).
When it comes to programming I often say that there is a difference between someone who has ten years of actual programming experience, and someone who has two years of experience that they just repeated five times.
I can state from experience that there are plenty of C++ developers who have been using it for decades who don't know basic things about it, and use it like it was still 1995.
[1] Deliberate Practice and Performance in Music, Games, Sports, Education, and Professions
> What you are describing
On the other hand, while I think you have the best intentions, I believe what you are describing is also the exact meaning of FUD. In that precise order: "in C++ they have serious consequences" is Fear, "There are bugs in the code--probably in your code--right now" is Uncertainty, and "Some might be CVEs" is Doubt.
There are so many moving parts in C++ there’s a reason why there are dialects. People are manually having to choose the smaller C++ that they can manage.
Is this the "70 percent of all security bugs are memory safety issues" article people like to link every time?
If so, it's not 2/3, it's 70%. They are not buffer overruns, but memory issues, and not all can cause remote code execution.
There is no rule that says that fixing bugs is an itch and everybody has to disperately scratch it, and some people can sleep well at night even if they have a few bugs. The rest is FUD in favor of one or another language flavor.
Not all software has a remote endpoint, is connected to internet, has an UI, or process input, etc. C++ and "juggl(ing) double-ended chainsaws on fire" is not the same and is an unfair comparison.
In C it's really easy, a trivial mistake, you just don't check the payload length and copy blindly, but in Rust you can't write this mistake at all, so you'd need to re-architect the system to make it possible to leak this data or else (if you're not actively trying to leak data) you just don't do this and you can't be attacked this way.
The closest analogue, Rust's [T].copy_from_slice wants a slice of the same size, so if we try to make the mistake OpenSSL made where we just forget to check, that means the slices are the wrong size and we panic, it's a Denial of Service but nothing more.
If the slice is the right size that's a normal heartbeat, everything works as intended.
This doesn't make a whole lot of sense. If you lack "time or energy" you're not going to put the extra work in to write unsafe code.
In C this bug was much easier to write than the correct thing, whereas I explained in Rust the bug is much harder to write than the correct thing. Humans are lazy so they're going to tend to do the thing that's easier, and here (and in many cases) that's the more correct thing in Rust but not in C.
This is just ergonomics. Notice how crash bars work on fire exits for example. Even people who are panicked and just running into the doors will trigger them to safely open outwards. You get incident reports where operators locked the fire doors, trapping people, or incidents where there are just too many people to evacuate despite the fire doors working for those who reached them, but you don't get incidents where people are like "Huh, I have no idea how to open this door, these crash bars are too difficult for me to understand".
> so you'd need to re-architect the system to make it possible to leak this data or else (if you're not actively trying to leak data)
OpenSSL called, your standard library sucks and it is going to provide its own significantly worse replacement for everything you can think of and at least ten things more.
> you just don't do this and you can't be attacked this way.
So it is a drop in C replacement.
You can do this, but, what are you copying and why?
The C code is just trying to copy the expected amount of data from the receive buffer into the send buffer. Under attack the receive buffer is actually nowhere near big enough to do that, but C doesn't care, which is why Heartbleed exists.
You can't write that mistake in Rust, even if you insist on painstakingly writing it out as a for loop, if we have a 20 byte receive slice, and we ask for receive[1000] that'll panic
To leak the data in Rust, you need to re-architect the software, you need to consciously plan for leaking the data in your software. "This code is to help us leak important secrets, and then this structure here enables the leaked data to be fed into data sent to an attacker".
FUD usually has a negative connotation as a disingenuous form of rhetoric. If your assume the poster is genuine, then you shouldn't in the next breath accuse them of spreading FUD. If one is genuinely fearful and uncertain and filled with doubt, it's okay to express that.
To the parent's point, C++ does allow programmers to easily write code that crashes spectacularly. Such bugs have been shown to cause catastrophic failure in critical systems, to the point where we decided to build languages and tools that mitigate those modes of failure. Those learnings have found their way back into C++, but the problem remains that writing modern C++" is an ever-moving target, and the "legacy C++" that should be avoided is still there in the name of backwards compatibility, so buggy code still being written. The solution is not "just write modern C++" because that doesn't work; witness the lamentations here about people who are still writing C++ like it's the 90s.
As for the uncertainty, the only problem with that statement is the "probably" because we all know the only bug-free code is trivial code (and even then...). But still, there's an important point here: shouldn't we be able to confidently make statements about our codebase like "there are no bugs of X type in here, because it's been checked by tools". For example, some languages are stricter with what they will allow past the compiler, and the level of strictness confers some guarantees about what kind of bugs have been checked. If we can't say "my language's tooling makes it so my program doesn't suffer from X bugs", then how much is it helping us really?
How hard have you tried?
The tooling is terrible to set up, and build system integration is extremely lacking. But once you have set up clang-tidy and clang-format, it just works, and it catches bugs and ensures a consistent style. And enabling clang's and GCC's static analysis is just a few compiler options away. Same with address sanitizer, leak sanitizer, UB sanitizer and thread sanitizer.
None of it gets used automatically, you have to understand the tools and how to integrate them into your build system. But they're not at all elusive.
Pick any medium/large project written in any language that hasn't undergone extensive formal verification+testing and this statement will likely be true.
A lot of cases which might invoke UB in C++ will just be a Denial of Service in Rust, as you panic because an unhappy path wasn't catered for properly in your software and most likely your panic handler just terminates the program. That DoS might still be a CVE, but it's probably not the severity you'd have seen for the equivalent C++ bug.
The “secret” obviously is 9000+ system level tests.
And I will claim that you need that number of tests. Even if the application was written in (say) Rust. Rust won’t save you from subtle daylight savings errors, country specific governmental requirements, weird special case organisational policy rules etc. Only an executable spec (tests) will do that.
I agree. Same here been using C++ forever, first as "C with classes" and then "modern C++" and whole bunch of other languages as well. I have general understanding about "big" parts but I learn specifics on on need basics and yes tooling / internet search helps greatly.
I manage to write software that works reliably and do not sweat over those FUD spreading prophets telling me that my software will self explode every other day.
After a decade, there was no end in sight on all the obscure patterns and way of doing things provided by the language. After a decade I realized that the more I learned about the language, the more I needed to know. And that's when I called it a day and promised myself not to touch this pile of smoking crap even with a stick in my career. I write code because I like to build things that solve real problems, not because I like to jerk myself off on all the possible theoretical ways to initialize a variable or a pointer.
If you need to read tons of fat books to be aware of all the nuances of the language, then the language has failed its goal of being a useful tool to build things.
And where are the advantages given by all the possible types of memory allocation, pointer declaration and variable initialization? C++ is still a memory-unsafe language, after all. So you still have to handle most of the low-level complexity of C, while getting a language polluted by four decades of patches and crap, and having to read big books just to understand how initialization and pointers work.
No wonder that C++ is increasingly becoming a relic confined to the cemetery of bad technological ideas, no new software is being built in it at all, and Rysy and Go are eating all of its cake.
You should probably judge usefulness the language by how much software was produced using it.
> No wonder that C++ is increasingly becoming a relic confined to the cemetery of bad technological ideas, no new software is being built in it at all, and Rysy and Go are eating all of its cake.
Do you have any data to support this? Where i work (one of FAANG) we don't use Go at all and rust is limited to only certain types of applications. Other stuff is either python or c++.
Then Fortran and Cobol are probably the most useful languages (and maybe Java can be on that list too), but can we say that they're actually loved by those who program in them, or that they offer a modern and fast way of building software? Eventually, how much software was written in a language is mostly a function of how long that language has been around.
> Do you have any data to support this?
Just pick any news article about any large projects migrating from C/C++ to Go/Rust. C/C++ have historical issues with memory management that make it too easy for the code to blow up. The vast majority of CVEs is about vulnerabilities caused by improper memory allocation, deallocation or assignment. And, in order to mitigate some of those issues, C++ has become so complex that it makes it harder to shoot your foot, but easier to blow up your own leg. Sure, all FAANGs have been writing most of their core software in these languages, but they've also invested a lot of resources in building alternative languages that were simpler but equally performant/expressive.
Keep in mind that FAANG companies have to onboard hundreds or thousands of engineers, and the cost of getting somebody to be proficient in Go, Rust, Python or Kotlin is much, much lower than getting somebody to be proficient in C++ - and, most of all, to get somebody in a position where they can reliably push code to production that doesn't blow up.
Judging usefulness of a language based on news article doesn't sound like a good idea to me.
> Sure, all FAANGs have been writing most of their core software in these languages, but they've also invested a lot of resources in building alternative languages that were simpler but equally performant/expressive.
Sure they invested in new languages but don't forget they also invest much more in existing ones.
There are a lot of critical bits of software written in Fortran. I don't think there is that much Cobol still running around though.
> and maybe Java can be on that list too
Of course java is an useful language!
I have never understood this POV, but to understand C++ O think you need to grok this key point deeply.
That preference to solve things in "the" library leads to other horrible problems though, where C++ ought to have a first class language feature, but instead WG21 has enshrined a workaround in the library which is probably better than if you'd made your own, but rather poor compared to a first class feature.
Take the array. Rather than provide a good array type in the actual language, C++ offers std::array, a library class in the standard library. But this standard library class isn't available in "freestanding" C++ while the builtin C-style array is available. So you end up with awkward syntax and the core language loses flexibility.
Likewise strings, in Rust the type of an arbitrary literal "Hacker News" is &'static str, a reference to an immutable view of this text which lives as long as necessary. In C++ the type of "Hacker News" is char*, a pointer to bytes. C++ 17 at last provided std::string_view which is the thing you actually wanted for about two decades, but the type of "Hacker News" isn't string_view it's still char*.
> In C++ the type of "Hacker News" is char *
Nope it is at least const char , and if you want to assign it to a variable it is:
const char * const hn = "Hacker News";
jesus, the hn formatting with this is driving me madMuch of the C++ standard library (including for example std::array) is not available in freestanding C++. Should it be? Perhaps. But it isn't.
In contrast to Rust's conscious decision to provide core, alloc and std as separate layers, in the C++ there's a rather ad hoc process to decide what's freestanding.
Err..string_view was available in Boost for quite a while actually - at-least for a decade as far as I remember, possibly more. There are so many missing features in Rust that are only available in crates even today: sane error handling for one/custom allocators for another, but god forbid if you criticise Rust on that basis. You will get downvoted to oblivion.
In fact it is not:
int main() {
auto&& x ="Hacker News";
x.foo();
}
<source>:6:7: error: request for member 'foo' in 'x', which is of non-class type 'const char [12]'
6 | x.foo();They are an unstable type (an array) that, because of its C affinity, tends to quickly decay to pointers, hence the need to use ̵a̵ ̵p̵a̵r̵t̵i̵c̵l̵e̵ ̵a̵c̵c̵e̵l̵l̵e̵r̵a̵t̵o̵r̵ the universal reference to observe its fleeting lifetime.
edit: can't speak English
The main thrust of my point though is that this type is terrible - even before it decays, and you don't get a std::string_view which is what you probably actually ought to get in modern software.
Animated image, no more comments needed
There's a lot of extra fluff like examples and such beyond the basics of C++ initialization.
Also seems to have relatively little text per page in the Amazon preview [1].
[1] https://www.amazon.com/dp/B0BW38DDBK?asin=B0BW38DDBK&revisio...
Have fun :)
Also you can write a full book just about C pointers, it's up to the author how far they can stretch it.
N.B. I also am not a huge fan of C++ but certainly for the past 10 years I think it's a net loss not using it.
If you look at how much surgery is required to make the Rust for Linux alloc work you can see he was quite serious. For example Rust for Linux Vec::push doesn't exist, because if the Vec wasn't big enough that's an implicit allocation, instead Vec::try_push is provided, which is fallible.
There was nobody proposing to even attempt that kind of work for C++.
I don't think his position is entirely rational (and, understand, I have otherwise huge respect for him as an engineer).
Niche knowledge is fantastic for those who want it or need it. No need to knock it just because you don't.
Please reconsider bashing it that much, if not out of respect for the people who use this tool daily, then out of the observation that the program you used to write this message was probably written in or uses libraries written in C++.
It's a powerful tool and has been used to create amazing things in competent hands, so maybe its flaws, that we like to bash so much, are a great thing - it's what drove people to create all this other languages that we love to experiment with.
I don't see much difference between pointing out that C++ is a bad idea despite the fact that some good software was written using C++ and pointing out that slavery is a bad idea despite the fact that some famous US landmarks were built using slave labour.
Also, "reconsider bashing it that much, if not out of respect for the people who use this tool daily", wow. Surely you don't mean that respecting people who are in a tough situation (e.g. having to write C++ daily) involves never pointing out that the situation they are in is, indeed, tough and could be better?
It's still semi-compatible with C, templates are about as close to macros as you'll get with a syntax; and the algorithms parts of stdlib are in many ways brilliant, thanks to Stepanov.
But the complexity is definitely overwhelming.
Specifically compared to C, there's a reason so many people switched to C++. They weren't sheep. They weren't just following marketing. If you don't understand what was better about C++, then you're criticizing from a place of ignorance, which is not likely to provide valid criticism.
Now Rust, Go, Python... there are reasons at least some people switch from C++ to those languages.
Here's the old post for reference:
https://lkml.org/lkml/2004/1/20/20
Also, one for git:
https://lore.kernel.org/all/alpine.LFD.0.999.0709061839510.5...
Here's what the book actually covers and it is "initialization and related areas" which you could fill many pages about any language if you include examples, a quiz, some foundational chapters:
The book contains 14 chapters in the following structure:
Chapters 1 to 5 create a foundation for the rest of the book. They cover basic initialization rules, constructors, destructors, and the basics of data members.
Chapter 6 is a short quiz on constructors. You can check your knowledge from the first “part” of the book.
Chapter 7 Type deduction: auto, decltype, AAA
Chapter 8 describes Non-static Data Member Initialization (NSDMI), a powerful feature from C++11 that improves how we work with data members. At the end of the chapter, you can solve a few exercises.
Chapter 9 discusses how to initialize container-like data members.
Chapter 10 contains information about non-regular data members and how to handle them in a class. You’ll learn about const data members, unique_ptr as a data member, and references.
Chapter 11 describes static non-local variables, static objects, various storage duration options, inline variables from C++17 and constinit from C++20.
Chapter 12 moves to C++20 and describes Designated Initializers, a handy feature based on similar thing from the C language.
Chapter 13 shows various techniques like passing strings into constructors, strong typing, CRTP class counter, Copy and swap idiom, and more.
Chapter 14 is the final quiz with questions from the whole book.
And there are two appendices: Appendix A - a handy guide about rules for compiler-generated special member functions.
Appendix B - answers to quizzes and exercises.[citation needed]
We are heading for a personal dystopia of mine everyone's attention spans are too short in every aspect of the life. People are addicted to new stimuli, whether it be the next 1 minute video in the timeline, or a new feature for the software that will never be "mature" due to all the other new features.
As for your second point, it's already been happening for quite some time. Can we blame people though? Every product we use is engineered to perfection to beat human psychology and make us indulge more and more. It's hard to protect yourself against something like that if you realise this is a problem, let alone if you don't. It doesn't just apply to technology but other areas too, take food for example.
I've been programming in C++ since 2004, involved in Boost then the standards committee. For me there hasn't really been such a big change, it's always the same language at core, with a few evolutions, that mostly make it slightly more terse.
Lambdas, just syntactic sugar for function objects.
auto, you could just spell out the type and provide a mechanism to deduce it (e.g. the old result_of protocol).
Move semantics, this is simply distinguishing lvalues from rvalues in overload resolution. There were other ways to do that before albeit not as accessible to the layman.
> Smart pointers are not a language feature, and are generally a bad practice (shared ownership is a bad idea to begin with).
Smart pointers don't imply shared ownership, see std::unique_ptr. Or is std::unique_ptr considered bad practice as well? That would certainly be news to me ;-)
https://www.cppstories.com/2023/init-story-print/
And the book at Leanpub: