6 ms·
There's the language as idealized, and the language as used. Stroustrup is clearly brilliant, but he's talking about the former while everyone else means the la
by kstrauser 3y ago
There's the language as idealized, and the language as used. Stroustrup is clearly brilliant, but he's talking about the former while everyone else means the latter. If you started a brand new C++ project today, using only the modern, safe ways of doing things and including only dependencies that do the same, OK, fine. That's, what, 0.1% of C++ projects? The rest of them use a soup of features and misfeatures that've been released in the spirit of trying to make everyone happy simultaneously.
I am solidly in the camp that believes C++ is unsafe. With enough discipline and tooling, it is possible to write safe C++. Are there more than a sliver of shops jumping through those hoops?
Of course, there may be a survivorship bias involved that proves me wrong. If it turned out that every remaining C++ shop is great at writing C++ code, because all the shops that weren't gave up and migrated to something else, I wouldn't be shocked.
- cqqxo4zV46cp 3y ago> If it turned out that every remaining C++ shop is great at writing C++ code, because all the shops that weren't gave up and migrated to something else, I wouldn't be shocked. I would. Almost all conversations about C/C++ security are, to this day, alive with people that are in my opinion delusional. The survivorship bias is more that the community is left with an over-representation of people that take “it’s often impractical for a team to write secure C/C++” as a personal attack against their intelligence and choose to dig their heels in as a result.
- moomin 3y agoBoy do I have some codebases I could show you. I mean, the biggest problem isn’t that they’re written in C++, but it really didn’t help either.
- lazypenguin 3y ago> With enough discipline and tooling, it is possible to write safe C++ I think it's possible to write "safe enough" C++ for real-world use. Then after some time you get a weird crash because MSVC stdlib implementation does something weird in new spec. Or your dependency does something unsafe and hoses you. Or the new guy uses `std::string_view` but forgot it doesn't guarantee null-termination. Or you casually forget about iterator invalidation because you're tired and compiler doesn't help you. Or or or. I like C++, it's a nice language but after befriending Rust compiler, or seeing how awesome hot reload in C# is or live-coding a front-end in javascript or any other advantages of other languages I find the use-case for C++ shrinking daily. Really if the C++ ecosystem wasn't so damn rich with libraries, tools and more I doubt it would have as much backing today.
- olliej 3y agostring_view doesn't guarantee life time of the backing buffer either. It's a fundamentally memory unsafe type.
- jsheard 3y ago> There's the language as idealized, and the language as used. As an aside from safety, I'd like to whine about how the "modern" solution for organizing C++ code into modules was standardized ~4 years ago after however many years of development and still barely works in practice on any of the three major toolchains. What good is solving problems on paper if the solution is apparently near impossible to implement properly?
- Calavar 3y agoI'd say it's actually the other way around. C++ in the abstract is full of half-baked, ill conceived ideas that later had to be deprecated or walked back entirely: std::auto_ptr and memory leaks; std::shared_ptr::unique() not being threadsafe; taking the time to implement bounds checking in std::vector, but the default access operator isn't checked; corner cases with std::initializer_list leading to unexpected behavior. In practice, most C++ shops seem to limit themselves to a small subdialect of C++ that's reasonably safe. The C++ Core Guidelines are essentially an effort at canonicalizing some of these best practices and building them into the tooling. I think it's fair to say that Rust or managed code should at least be considered for any greenfield project, maybe even a hard requirement for certain domains like cryptography, networking, financial applications, etc. But I don't believe that every application needs to be security hardened to that level, and the C++ library ecosystem is still much more robust than Rust's is. Crates.io feels like a graveyard of experimental libraries that reached 40% the functionality of their C++ cousins before the commit activity dropped to zero.
- marcus0x62 3y ago> Crates.io feels like a graveyard of experimental libraries that reached 40% the functionality of their C++ cousins before the commit activity dropped to zero. Not only that, but you see a huge increase in external dependencies because of the ease of importing crates. I'll admit I have absolutely no evidence to back this up, but the crates system feels to me like the Achille's Heel of Rust's security model in two respects: 1) There's the obvious supply-chain risk in that the provenance of most of these crates is...uncertain at best... At best you can see some stats that x number of projects use the crate, and who the owner is or at least purports to be. 2) Having eliminated most memory-related vulnerabilities (or at least constrained them to unsafe blocks,) the remaining vulns are going to tend to be logic flaws, and Rust, of course makes no guarantees in that realm. If you import 1 crate, ok, you can probably audit that crate and maybe reason accurately about what your code is really doing. But, when you import a half dozen crates, and those crates have dependencies, and so on, and you end up with 100+ external dependencies, I would argue that reasoning accurately about the behavior of your code is going to be quite difficult. To me, this is a cultural problem/blindspot with Rust that will be difficult to fix.
- nox101 3y ago> If you started a brand new C++ project today, using only the modern, safe ways of doing things and including only dependencies that do the same, OK, fine. My impression is this is not effectively true. It's not easy to follow all the rules of safe C++. I could be wrong but things like forgetting to use std::move in the right place or forgetting to use unique_ptr where you should. Putting member initializers in the wrong order. Forgetting a copy constructor or forgetting to hide the default, etc... I'm also not sure of mapping structures to binary data (maybe this isn't enforced in any language but ...). Let's say you want to read a BMP file. You declare a struct that matches the header. If you declare every byte it might work but if you declare it as 16bit values and 32bit values then IIUC, padding rules extra make your code non-portable. What rule enforces that you don't write non-portable code? Also, what rule enforces no undefined behavior, like say int overflow? The point isn't that it's possible. The point is it has to be enforced by the compiler. Does that option exist `--safe-cpp-only` or something like that the enforces 100% safe usage only? (and obviously, just like rust, you could break out when you need to?)
- kstrauser 3y agoPerhaps I should've prefaced that with "for the sake of argument, let's assume...". I think you're right. But even if we're wrong, and modern C++ is bulletproof, that doesn't matter because very little code is written with those idioms.
- tialaramex 3y ago> Let's say you want to read a BMP file. You unquestionably shouldn't use C++ or Rust, you should use WUFFS to write this portion of the software, this way you get the absolute safely you presumably expect and also you get much better performance than you'd get from hand rolling say C++. You will need to write extensive test suites since the WUFFS tooling doesn't know what a BMP is so your testing is the only way to know you're decoding it correctly - but the safety property drops out of the language design, and while performance isn't magically guaranteed it's much easier to write small fast code in a language designed to help you do that and which also catches every safety mistake you make in the attempt.
- benreesman 3y agoC++ codebases are substantially about vintage. C++03 with this and that vendor-specific thing is still in the wild and those codebases are just tough to fix: it’s clearly possible, Google has a bunch of C++ from that era deep in the heart of things and they’ve managed to keep it not only running but to all evidence performant and secure. There are some financial shops that seem to have walked a similar line. But the typical C++ codebase from the early/mid 2000s isn’t something you’d sign up to own. It’s a pretty elite C++ shop that has managed to walk stuff from 98 or 03 to the present day without a real pile of grief. And it’s eminently understandable that Mozilla was looking at a codebase from that era and said: “it’s time to consider drastic measures”. But C++11 eventually got codified and adopted, clang-tidy and the sanitizers came on the scene, the screws got tightened yet further with 14, and 17, and 20 which is now substantially supported. It’s just not black and white anymore: C++ has real interop/FFI and a largely “opt-in” mentality around the extreme low defect approach, Rust has a much more opinionated posture that’s trivially better at getting extreme low defect outcomes at shops more diverse in experience and rigor than old-school FAANG or Microsoft but botches some key stuff, there is not a one-size-fits all answer here. Or rather there is if you don’t over constrain the solution space: if extreme low defect is the call you want a real type system. At the less mainstream end that’s probably Haskell or OCaml or something of that lineage. But ultimately the Quality Inequality (good fast cheap pick two) remains binding: and TypeScript is just trivially where you find a lot of people who grew up with advanced dependent typing and don’t cost a million bucks a year each. It’s faster than most would guess, and the type system is up there with Idris or something like that in terms of the scope for machine checked rigor: these are MSR people, would anyone expect less? The conversation is over-rotated on Rust vs. C++ which is a complicated question to which few need an answer.
- germandiago 3y agoOn the other side, Rust is a safe language that in real world uses unsafe blocks and unsafe libraries underneath (OpenSSL and other C libraries in practical terms). That is not safe either in practical terms. So there is always this discussion about putting C++ as an unsafe thing and it depens a lot, as you said, on how you use it. I use max warning level, warnings as errors, smart pointers, almost everything return by value and sanitizers. In Rust I have the advantage that libraries can be audited for unsafe blocks, but it still has unsafe and it will still use unsafe libraries in practice from C. So I always challenge these people that say Rust is safe. It is when it is. In rea life it is not perfect. I am pretty sure that the distance gap in safety from well-written C++ and Rust is, well, very small.
- edflsafoiewq 3y agoCertainly it is a question of degrees. But to say the gap in safety is very small is essentially to say Rust's lifetime system is close to zero-value. Unsafe code still exists, the goal is to localize it to small, auditable blocks. However safety often depends on non-local properties. For example, unchecked iteration over a vector requires we know the vector's lifetime exceeds the iteration lifetime, and that the backing buffer won't change. Rust's lifetime system allows this to be expressed, so the "critical zone" can be localized within a single block in the library implementation. If the library is correct, all consumers are safe. In C++, these cannot be expressed, so the critical zone for this safety guarantee is smeared across all code executed during the iteration. No consumer is guaranteed safe.
- germandiago 3y agoIt is not that the borrow checker is zero value. But the borrow checker enters the game also depending on your code style. And when it does it gets quite viral with annotations and the like. That is why I like more a model like the one found in Hylo language. It sidesteps the rigidity of the borrow checker and it can do very well. Also, the code style I try to keep in C++ is quite "value-oriented" to avoid the typical lifetime pitfalls. Nowadays I try to use STL with debug mode but not escape iterators. I am actually considering using a library like Flux because I think that, as fast as the iterator model is, it is basically playing with fire. Flux seems to be very competent performance-wise and safer, besides compatible with ranges. I am quite happy with C++ at the moment. That does not mean I won't use Rust in the future. Who knows. I hope more work towards safety will be fone in C++. I would not want a borrow checker, but avoiding as many pitfalls as possible is a good thing. I always use compilers with the maximum warning level and warnings as errors. It is actually a good thing to do.
- kazinator 3y agoTo write safe C++ you need moral discipline, rather than intellectual/cognitive. The language has the tooling. Moral discipline means rejecting the use of unsafe constructs for the sake of speed or convenience, and doing the safer, slower, sometimes more clumsy thing. For instance, to skip thinking like "oh, we can just retain a direct pointer into here, and then we don't need to make a copy ...". And, of course, while that is all well, you can only practice this advice only applies to small team or solo greenfield projects.
- kazinator 3y ago> Stroustrup is clearly brilliant, but he's talking about the former while everyone else means the latter. Where is he doing this? His main statement doesn't bear out this accusation: "“[t]here are two problems related to safety. Of the billions of lines of C++, few completely follow modern guidelines, and peoples’ notions of which aspects of safety are important differ. I and the C++ standard committee are trying to deal with that.”