Modern C++ has made big inroads into removing memory safety issues though. Memory safety bugs are relatively uncommon in modern C++ compared to pre C++11 times.
You could argue that C++ is still "unsafe by default" and you'd be right. But most C++ programmers use smart pointers and unique pointers rather than raw pointers now, vectors instead of raw arrays, etc.. And it does make a big difference.
std::string foo = "foo";
const std::string foo2 = absl::StripAsciiWhitespace(absl::AsciiStrToUpper(foo)); // in Python: foo2 = foo.upper().strip()
std::cout << foo2; // garbage
Yeah, all technically the user's fault, but it gets pretty tiring worrying about this stuff when you're writing a high-level application and the few CPU cycles you save don't really matter. No matter how good you are, you will make mistakes, and the chance of one bug corrupting memory in your whole process increases exponentially with the LoC written. Also I have to point out the irony in a language being type-safe but not memory-safe.
Also, totally user error, but I didn't expect Java-looking class syntax to initialize member variables to garbage by default. That should at least be a compiler warning.
Misnomer aside, there is no contradiction. Type safety is much easier to reason about at compile time than memory management. So, it shouldn't come as a surprise. You need a rust like "borrow checker"-by-default functionality.
The contradiction is when the veteran SWEs I work with say we use C++ instead of Python for the added safety of compile-time type checks, treating dynamic typing like a bigger risk than memory trampling (never mind that Python has type-checking too if you really want it).
C++ does have issues. However few languages can handle complex problems well. (Rust is very intriguing for the possible ability to do things at the same scale)
If you use the wrong type in Python, you get an unhandled exception failing to find a property or something, that's about it. Maybe there's a contrived scenario where it'll succeed in a wrong way, but again that's a smaller blast radius
C++ has exceptions too, but I've never used them, so idk. The abseil statuses end up being used like DIY exception handling, and it's ok but a bit easier to mishandle something than in Python. And a segfault cannot(? or shouldn't) be caught and will crash the whole program.
For short programs Python works great, but it doesn't scale to large programs.
The problem is C++'s priorities. Priority #1 is performance, and if you look closely, nearly every single instance of this buggy-program hostile attitude comes down to the language and libraries absolutely refusing to do any kind of dynamic safety checking by default. UB can be traced directly from "it's too hard to reason about the implications of buggy programs" to "well, don't do that, stupid programmer".
I'm no expert in this, I'm just a guy who's sick of writing web backends in C++ for no reason.
No. It's not. It's designed by committee -- impossible to deprecate old misfeatures, trying to entertain everybody but please nobody.
<source>: In function 'int main()':
<source>:6:54: error: conversion from 'absl::string_view' {aka 'std::basic_string_view<char>'} to non-scalar type 'const std::string' {aka 'const std::__cxx11::basic_string<char>'} requested
6 | const std::string foo2 = absl::StripAsciiWhitespace(absl::AsciiStrToUpper(foo)); // in Python: foo2 = foo.upper().strip()
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
ASM generation compiler returned: 1
This is gcc with c++20, curious what the garbage is you're describing.The garbage comes from AsciiStrToUpper returning a new std::string which StripAsciiWhitespace takes as a string_view (implicit conversion). By the time you print foo2, the string is already freed.
For those downvoting, please explain why you think string_view should take ownership of an rvalue string
I didn't downvote, btw. I don't do that in general.
Just turn people loose on your codebase without supervision and be really surprised that the hackers make off with your data?
You, sir, are arguing from bad faith as your obvious mission is to promote “rust in all the places”.
But sure, you can still make the error with expiclit types.
But "Herb Sutter says you should use it, he even gave it a catchy name/acronym" is as good as it gets. And he does so AFAIK in the same place he created the "Almost Always Auto" name: https://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style...
Notice that he says "the main reasons to declare variables using auto are for correctness, performance, maintainability, and robustness—and, yes, convenience, but that’s in last place on the list.". He is arguing literally the opposite of what you do, that using auto avoids bugs ("for correctness").
I'm not going to argue in favour or against "Almost Always Auto". But I see no problem calling it "Modern C++".
* Herb Sutter being the convener of ISO WG21
Even if you use string_view on the LHS instead of auto, pretty easy to miss the bug here.
It's even better if your language has a way to express "the return value points to data from the input argument, so it's a compile error to pass a rvalue string to this function". The second we got a language able to do that, usable everywhere where C++ is, (yes, that one) the incapacity of C++ to express this became "a problem with C++". Our expectations have just increased.
Surely it can be catched via static analysis if you suppose the common case that the return value is a function of the argument, and not pointing to some static global data. But you will get false positives when somebody does the uncommon case. There is a lack of expressiveness in C++ here.
Last I remember, the lifetime profile stuff was there, but there was still no way to add your own annotations. For some reason, I didn't hear too much about any of this, it was
- clang is working on it
- clang is working on it
- clang is working on it
- Visual Studio has it
- clang is working on it
- Silence
gcc still has nothing, right?
There are so many details you need to keep track of, and eventually you make a mistake. Now, yes, i know what all of that does, and how they are supposed to be used, but it does not remove the cognitive load.
Many languages have a substantially lower cognitive load when doing something trivial, such as ToLower().ToUpper(), etc.
Let’s not make this a language war, there has to be some programming language that does low-level memory operations without batteries included for some types of technologies. When I write code in C++ and I want something like garbage collection or ref counting I can reach for a shared_ptr.
If you don’t want to concern yourself with types, values vs references, or manual memory management don’t choose C++. The default handling is sane if not necessarily intuitive. You shouldn’t create a ref or pseudo-ref (string_view) to data that is not on the heap and no longer allocated to the stack - seems sane. This problem could be easily caught by breaking function calls into separate lines and explicitly specifying the types at each step.
From Xerox point of view, yes. Unfortunely they lost to the UNIX workstation market.
Interlisp-D, Smalltalk and Mesa/Cedar.
Believing that one size fits all is a sure way to alienate a lot of people who don't fit that size. Right tool for the right job and giving the freedom for people to solve their problems in the best way is a better way to win people over.
Nothing to do with the technology.
Hence why I love why Android is so locked down to native code, and Apple is doubling down on Swift.
Google also has a lot of stuff in the middle where they're fine with sacrificing a little speed for a lot more safety, but Java etc are too slow. Golang was supposed to satisfy that, but it was too big of a leap from C++.
Is Cpp systems-level language? As I see, the problems arrives from the fact that Cpp is a multi-paradigm language, and as such contains a near endless wealth of features to combine. Also modern Cpp takes a clear direction away from low level to (relatively) more high-level language. The problem really comes from the combination of low level and high level paradigms in the same language/codebase, where the expectations of the behavior of each piece of code can vary wildly.
> ... will have these sharp edges handled differently
Perversely, plain C, often has a lower cognitive load than Cpp. This is because you must always manually handle pointers, types, object lifetimes, etc. Now, this does result in a high cognitive load, BUT, the cases as in Cpp where you might combine auto, with a temporary, with a pointer inside, referring to some unseen resource, cannot happen, because you cant do that in C. So the worst case in cognitive load is never as bad as in Cpp. The lack of features means less possible combinations to shoot yourself in the foot. (Though C does still have many, nor is C an ideal language)
> Let’s not make this a language war
Discussing the problems of Cpp is not language war. Nor is understanding the merits and problems of the Design of Cpp.
> If you don’t want to concern yourself with types, values vs references, or manual memory management don’t choose C++
But IF YOU DO write Cpp, there is no escaping them. Which is my point.
Now, if you wrote absl::string_view foo2 = etc., you'd have a dangling view for sure. In practical industrial usage, you'd build with an address sanitizer (it's built into clang: -fsanitize=address), which should catch that issue.
We use an address sanitizer in tests but not in production. IIRC we had one untested log output line like this that caused corruption in prod.
Note that std::vector's at() predates C++11, whereas std::span was added in C++20. It's a myth that C++ is getting more memory-safe; in many aspects it's actually regressing.
In a greenfield project where you control every line of C++ code, you can easily achieve very good safety using nothing but ancient C++ features.
Not just memory safety. I mean, you can make your own numeric types that do overflow checks and throw exceptions or whatever. It probably won't be very fast, but it will be solid.
People have used C++ (ancient C++) to make numeric types with units, where you can't add "kilograms per second" to "meters". I remember that from some talk thing I went to in 1999.
All three major C++ standard library implementations have an option to enable assertion checks in both std::vector and std::span operator[]. Enable them unless you are in such a resource limited environment that you can't afford the checks (unlikely). The only reason the standard doesn't require the checks is to allow C++ to be usable in those resource limited environments. You could argue the checks should be enabled by default, but that's not something for the standard to decide, complain to your standard library vendor.
Modern C++ adds new features that allows you to write code that doesn't have as many memory safety problems, but all the unsafety (is that a word?) that existed in the past still exists.
Removing features that allows memory unsafety will probably never happen, because one of the main reasons people use C++ is all the legacy code will still be used for decades.
It's great that you can write C++ using only the newest features, and that does increase safety. But that doesn't offer the same guarantees of languages designed to simply not have these kinds of problems, or that by design flag the points in the code where safety can't be guaranteed by the compiler.
If the code is small and doesn't change or doesn't have to last for too long, that's OK. But when it's changed over the years, it's pretty much impossible to guarantee that the unsafe parts are kept small and contained, especially as programmers leave and new ones come in.
So is there a compiler flag or external linting tool that can generate warning messages when you're using an older memory-unsafe technique where a safer and more modern technique could replace it?
I remember when people said the same thing about C++03. Plus ca change.
In C++98 you could develop a program with some unsafe core of whatever resource management you wanted, and then wrap it with the right classes so that it can't be misused.
After circa 1995, anyone struggling memory safety issues in greenfield C++ code that was completely under their control (no legacy) had to be a supreme goofball not to be leveraging the language to make that sort of thing go away.
The "Ragel" tool they used evidently generates C that uses raw pointers:
> The Ragel code is converted into generated C code which is then compiled. The C code uses, in the classic C manner, pointers to the HTML document being parsed, and Ragel itself gives the user a lot of control of the movement of those pointers. The underlying bug occurs because of a pointer error.
The bug in these Ragel-generated parsers was somehow hidden or compensated for by some buffering strategy, which they tweaked when introducing some new kinds of parsers "cf-html". Those didn't have the bug, but the different buffering turned on for them exposed the bugs in the Ragel based parsing.
https://web.archive.org/web/20170223233000/https://blog.clou...
I'm looking at the Ragel State Machine Compiler user guide. Chapter 5 (Interface to Host Program) makes it quite clear what sort of thing the Cloudfare people chose to grapple with. Ragel will write code for you planted into middle of any C function anywhere; you must provide numerous predefined variables, some of which are pointers to data, and so it goes. For some languages, there are safer interfaces: for Java and Ruby there is a buffer and instead of a pointer there is an index into it. Ragel could have been upgraded to have actual C++ support of some kind.
Cloudbleed was found out in C code, with the usual issues dealing with bounds in C.
The effort needed on tooling would be significant though. I don't see that happening and overtaking Rust.
(btw the correct spelling is Achilles, Achilleus, Akhilleus, Ἀχιλλεύς)
VC++ has some annotations on that sense, problems are two fold, get the vendors to agree, and the C subculture that security actually matters.
Thanks for the correction.
For example, with -Wold-style-casts you can diganose every use of the (TYPE) EXPR casting notation, which is often seen in the lower-level C-like C++ code for punning memory.
Somewhere in some commonly included header for the project you can write declarations for C functions that should not be used, marking them deprecated. Then if people introduce strcpy or malloc or whatever you don't want, that can be diagnosed (and can fail compilation, if desired).
> It does not have many, but it has one and it's big. It's not memory safe.
It's not as big as a deal as c++ opponents make it out to be. The ratio of memory bugs to other bugs at work is basically zero. The problems that are holding our product back have nothing to do with memory safety. Of the long list of problems we need to solve, that just isn't one of them. If the only thing a new language offered over c++ was that I don't have to think about that 0.01% of bugs anymore, but in exchange I get either a garbage collector or a borrow checker to fight with, I would not switch.
Enough of this already. Not going to discuss ancient code. I currently using modern C++ and it has enough features to keep one's code reasonable safe if that was the goal. And frankly I do not remember any reported bugs in my production caused by "memory unsafety". Just plain old logic errors every once in a while.
The computer isn't "memory safe." If you want a language that can express every function of the hardware it runs on, you expose this fact as well.
Careful management of "unsafe" blocks might be an answer, but I suspect we're going to have to dig way deeper until we find a truly "safe" solution. At that point, would it actually matter which language you use?
https://www.chromium.org/Home/chromium-security/memory-safet...
https://msrc.microsoft.com/blog/2019/07/a-proactive-approach...
You can see the clear conflict in the mentality of these new languages. "Fast and, somehow, safe!" It's missing the forest for the trees, much like your retort.
People are. Like on Android.
> In Android 13, about 21% of all new native code (C/C++/Rust) is in Rust. There are approximately 1.5 million total lines of Rust code in AOSP across new functionality and components such as Keystore2, the new Ultra-wideband (UWB) stack, DNS-over-HTTP3, Android’s Virtualization framework (AVF), and various other components and their open source dependencies. These are low-level components that require a systems language which otherwise would have been implemented in C++.
> To date, there have been zero memory safety vulnerabilities discovered in Android’s Rust code.
> We don’t expect that number to stay zero forever, but given the volume of new Rust code across two Android releases, and the security-sensitive components where it’s being used, it’s a significant result. It demonstrates that Rust is fulfilling its intended purpose of preventing Android’s most common source of vulnerabilities. Historical vulnerability density is greater than 1/kLOC (1 vulnerability per thousand lines of code) in many of Android’s C/C++ components (e.g. media, Bluetooth, NFC, etc). Based on this historical vulnerability density, it’s likely that using Rust has already prevented hundreds of vulnerabilities from reaching production.
Source: Memory Safe Languages in Android 13 https://security.googleblog.com/2022/12/memory-safe-language...
But sure, keep telling yourself that it’s possible to write C++ code of this quality by being more careful or using some magical sanitizer or by hiring better developers or whatever.
Linus Torvalds - 2022
Even in my comment I quoted a passage that said "we don’t expect that number to stay zero forever". This is important. Although it has been successful so far, there will be bugs in it, even the odd memory safety bug.
That's still progress! Fewer bugs than before is progress, (mostly) eliminating a class of bugs is progress. I only addressed a person who was saying "there will still be some bugs, so there's no point tackling this at a language level". They're unable to grasp the idea of progress.
It does not have many, but it has one and it's big. It's not memory safe.
Check out this talk by Herb Sutter, around minute 43:
[ Note: in this context, the "government" is the US Department of Commerce and NIST, which, according to Herb, have issued a detailed guidance in response to the Executive Order on Cybersecurity [2]. While I couldn't find that guidance, I found one by the NSA [3], which saw quite a few comments here on HN in December last year [4] ].[1] https://www.youtube.com/watch?v=ELeZAKCN4tY
[2] https://www.whitehouse.gov/briefing-room/presidential-action...
[3] https://media.defense.gov/2022/Nov/10/2003112742/-1/-1/0/CSI...
[4] https://news.ycombinator.com/item?id=33560227