“Safe C++ Subset” Is Vapourware
robert.ocallahan.org
robert.ocallahan.org
However a programming language is just so much more than the language itself.
So until Rust can make up for the libraries, IDE integration, GUI libraries, ability to use COM and WinRT as if they C++ classes (C++/CX, C++ Builder, Delphi), mixed mode debugging like C++ has with JVM and .NET IDEs...
Many of us that need to stay in C++ land would rather have a bit of Rust security around our tools, than having to leave all that behind while Rust libraries continue to make use of Rust nightly just to get that tiny cool feature X.
If this isn't the case, it is how it looks form HN posts. Last case being one doing so for the Regexp macros, I just forgot the name.
I know how hard this is, after all I am part of C++ community since the C++ ARM was the only language standard available and used to be part of Turbo Pascal, Modula-2 and Oberon communities.
However, at least the C++ community is trying to make the language safer even if due to Rust's pressure.
Not like the C community that the best they have came up with was the optional Annex K (Bounds-checking interfaces), which doesn't offer any value. Pointers and lengths are still used separately anyway.
As you correctly point out at first, the value of a language is more than the language itself, it's also the tooling and the environment. And since you can't retrofit safety in the language, it's a good place to work on it.
Could you elaborate on what complexity you are referring to, and what makes it exponential?
Re: nightly features, I agree that there are several things which Rust really needs to stabilize to achieve many of its goals (stable SIMD, inline assembly, procedural macros, stable custom allocator support, the Rust Language Server for IDE integration, etc etc etc). Now that the major compiler internals refactoring of the last year is nearly complete, my understanding is that we may start to see more aggressive progress on some of those items which keep pushing users to nightly Rust.
I certainly think there are other cases (serde immediately comes to mind) where the best developer experience can only be had on nightly, and that's a big problem for wider Rust adoption.
Nobody needs to build valgrind for rust (or your favorite alternative). Leveraging existing libraries isn't trivial, but it's not a year long project either. IDE's support a lot of languages out of the box. rust support will be rough at first, but quality will be proportional to use. I suspect microsoft could build the layers you want like they did with J++ very quickly. A year? maybe 2?
The biggest bottleneck, imho, is developers. I don't think many people really know all the dark and scary corners of C++, but there are a ton of people that can get stuff done in C++.
If schools start graduating people with rust experience, the writing is on the wall for C++. Shifting school curriculum takes a big reason. Maybe a hit indie game, maybe some other killer app. I don't think servo is the one.
Really, it's a testament to the quality of the language that the complaints are all along the lines of "it's fantastic, but the tooling..." Don't underestimate that. Tooling can happen real fast.
Is rust a sure thing? no. absolutely not. does it have a chance of becoming the systems programming language in 10 years? Yeah. i think it does. i think it's got better than a 5% chance of doing that. 1 in 20 is a perhaps an insanely high number. The language is just so very good.
However, and that is somehow what I wanted to point out, by being part of the community I am fully aware of the uphill battle that was to make C++ relevant as it is today.
We used to be flamed by the C community, like like Rust, D, Go and others are nowadays, yet the C community now has their major compilers being written in C++, funny how things turn out.
So, yeah Rust or other saner systems programming language might eventually replace C++ and I would like to see it happen.
But bashing the efforts of the ones that try out to make C++ safer within its constraints, because not everyone of us is able to throw out our tools and move right away to Rust, is not going to help getting those developers.
For example, I would rather use C# + Rust than C# + C++/CX, but I am not going to convince any customer to allow me to do that.
It is pretty frustrating when you actually want to exchange on this language, far from dead, and enjoyed by some.
This thread is not refuting this claim but explicitly or implicitly blames TFA and "the Rust community" for misrepresenting C++ as dead, useless and not worthy of improvement.
Of course C++ is far from dead, enjoyed by some, and used by many more; TFA did not claim otherwise. Of course improving C++ helps those using it; TFA did not claim otherwise. All it claimed was that the current and potential future state of Rust relatively to C++ was misrepresented by people at the C++ side; I think you're more supporting than refuting this claim.
[1] I personally, unlike TFA and some commenters here, must say that I'm not thrilled about anything having to do with unique_ptr (which as TFA demonstrated is not guaranteed to be unique at all, but is as unique as its user made it) and other such; certainly if I have to rewrite an old code base to use new smart pointers, the change will involve every corner of that code base, and I think I'd rather rewrite it in another actually safe language if safety is what worries me. But that's just me; and BTW I haven't personally looked deeply enough into Rust to advocate for it, either.
I am talking about the HN topics like "How to implement a packet tracer in C++" or "Optimize std::vector with C++14" where invariably a comment like "Who use C++ in 2016 when you have Rust?" spawns. Rust is amazing, but that doesn't make it the de-facto answer to all the projects. Some people might still want to increase their skills in their day-to-day language like C++, even if they are aware of Rust awesome features.
A professor of mine used to talk about (i assume he still does) how software is like a plant. New software is always fun and cute and understandable like anything young, babies, puppies, kittens, whatever. C++ is like an oak tree. there are gnarled branches, but is quite beautiful taken as a whole. Like an oak tree, it stands the test of time.
The rust community has been ponderous in adding features. I think they've picked a really nice set of stuff to support. Perhaps rust will turn into a beautiful old oak, perhaps not.
I'll stretch the metaphor a bit more, C++ doesn't have as much room to grow. It's far from done, but there's a lot of legacy code that must be supported. Choices were made decades ago that limit options today. Again, this isn't meant to malign C++, it's just how hindsight works. Rust on the other hand has has the benefit of hindsight. Rust will make plenty of mistakes, but the community can avoid some of the trouble C++ has had simply because it's new and has more information about what's really useful and what's not. The rust community has by and large been very aware of what works and what doesn't. They're careful gardeners. They've learned their history, and want to try other stuff. Perhaps rust can grow to be a mighty oak like C++. I hope it will, but that remains to be seen.
The Rust community is one that generally is extremely supportive of new users, even when those users are very pissed off at the steep learning curve with lifetimes, etc. It's impressive to see some of the even handed responses to questions.
Reiterating what others have said, no one is saying C++ is dead, no one is saying that C++ won't be around for a very long time. What people I think are saying in general is; if what you want is a memory safe, data race safe, strongly typed (with ergonomics) language, Rust is a great one. It's got lots of really nice modern semantics, and comes at the same performance cost as C++ (in general).
It's like a Tesla vs. an awesome classic Ferrari; no one wants that Ferrari to die crashing out a window, in fact any car lover wants to see that thing continue to get a lot of love and work to maintain it. But maybe there's a nice car that would be better to drive day in and day out, so that you don't have to worry as much about a parking attendant taking if for a joy ride.
Anyway, I suppose that rust can still benefit from other valgrind tools, like helgrind, drd, massif and so on.
The thing is... I don't think Java took over C++ developers. Most likely they took over Pascal, Delphi, VB, Perl and other enterprise developers.
Every decade there is a language that claims to kill C++ (Java in the 90's, Go in the 2000's) offering features that are not bothering C++ developers and end taking off by attracting developers from other communities (like Go with Python devs).
I believe that memory safety or manual memory management is not the mainly concern for a C++ developer. On the other hand, if you give the same tools that C++ has and reduce complexity and then you might got into something, until then...
I don't think we'll ever see a mass exodus of C++ developers to other languages - but in truth we never saw one from Pascal, Delphi, VB, Perl or Python either. What we will see is the niche where C++ is appropriate gradually shrinking, the borderlines between what's appropriate for C++ and what's appropriate for Java etc. changing, until one day we notice that there's hardly anyone writing C++ anymore. E.g. 20 years ago people would have said there was no way you could write a game except in C++ (or assembly). 10 years ago people would have said ok, you can write some games but not an FPS. Nowadays people say ok, maybe you can write a high-performance game without writing any C++ yourself but the engine you use will still be written in C++...
Amazon went from C++ (to Perl) to Java.
Google went from C++ to C++/Java (and now Python and then Go)
Microsoft went from C++ to C++/C# , C# is Microsoft flavor of Java.
Microsoft is a bad example because they pretty much do everything in C++ (Office, Windows, VS, etc).
Also by enterprise I meant banks, insurance companies, factories, etc.
(0) By far, the most important use case for templates is emulating parametric polymorphism. The solution is obvious: provide actual parametric polymorphism.
(1) The other important use case for templates is performing compile-time computation. This is done either by pattern matching on types (which parametric polymorphism forbids) or on values (e.g., integers) promoted to the type level. The latter feature is available in Haskell (`DataKinds`), and its design can be borrowed mostly as-is by systems languages.
(2) Classes combine, in a single language construct, features that ought to be separate: user-defined types, encapsulation (access specifiers), code reuse (inheritance), etc. Again, the solution is obvious: split these features into different language constructs: structs and enums, module-level privacy, parametric polymorphism, etc.
(3) Ad-hoc polymorphism is useful, but overloading as done in C++ is too unconstrained to support modular type checking of users of overloaded operators (templates). Again, the solution is known: type classes.
Also just we C++ 90's hipsters were doing systems programming in C++.
Most developers were finally getting convinced that in home computers C, Pascal dialects, Basic compilers, Modula-2 and so forth were getting good enough not to depend on piles of Assembly code.
C++ was getting used in GUI toolkits, CORBA and DCOM and seen as too high level.
Hence why the commite tried to specify the Embedded C++ standard, to cater to the more hardcore C developers.
I only used it in 1999, because our teachers at the university wanted to use this new cool language from Sun.
At banks. insurances and telecommunications businesses, we were all doing C++ with CORBA and DCOM, using MFC and VB 6 as frontends on the desktop.
The amount of Delphi users were already ramping down thanks to Borland's mismanagement.
There are still a niche for C++ in banks (i.e transactional server applications) but pretty much all the business logic applications (that counts for 80% of the code of most banks) were starting turned to Java by that time
Previous attempts include Microsoft source code annotation language (SAL), Cyclone (AT&T research language), SCC, the Safe C compiler, Ccured, and MemSafe. None really went anywhere.
What this probably means is another sort-of-safe dialect of C++, accompanied by hand-waving and PR.
C++ is trying to do single owner semantics, but without something like Rust's borrow checker, the best you can do is crash at runtime on violations of move semantics. You have to design the language around borrow checking to get ownership right.
Still, it's good that the C++ crowd is trying. A decade ago, I was trying to get the standards committee interested in memory safety issues, but they were into exciting new template features used by few.
> https://www.cis.upenn.edu/~eir/papers/2013/ironclad/paper.pd...
It still relies on dynamic checks. So you still have the same problem as in Java: just because you have a reference, it doesn't mean you actually have a valid object. You can make any language trivially safe by checking absolutely everything at runtime, but the whole point to using a language like C++ is to not incur in unnecessary performance overheads.
> http://research.microsoft.com/en-us/um/people/marron/selectp...
Have you actually read the paper? This isn't a static analysis at all. It just makes a dynamic analysis more eager and precise.
Additionally, since Windows 8 C++ is allowed on the kernel space and they have re-written the C library in C++, exposing the API via extern "C".
None of these projects were trying to define safe C subsets. SAL is a system of annotations for static analysis tools. SCC, CCured, and MemSafe were all attempts to add dynamic checks for memory safety to C; they are the ancestors of things like the Address Sanitizer. Cyclone was a new programming language, and it innovated the system of ownership and borrowing that is used in Rust.
Have you seen Sutter's video where he presented the static analysis that the Visual C++ team was working on? The whole point is that with some additional code changes (especially around pointer usage) one WOULD detect errors at compile-time.
When and if this static analysis will be released remains to be seen, but this blog post sounds petty.
The point isn't just detecting errors, but rather completely disallowing code that does unsafe things, by turning every undefined behavior into a compilation error. Unfortunately, determining whether a C++ program has undefined behavior is equivalent to solving the Halting problem.
The Core Guidelines analyzers do not try to accept all valid C++ programs, or even a large percentage of real-world C++ programs. In principle they will accept a subset (+ annotations), a language within a language, designed to make it possible to statically verify memory safety, similar to Rust.
I'm aware. My point is: Are the Core Guidelines enough to completely rule out memory errors? This is essentially a type safety claim, and thus requires (possibly informal) proof.
Does it matter? As long as they rule out the vast majority of memory errors, e.g. many times more than static analysis tools find today.
Sigh. You can write undecidable programs, but you don't want to. This issue has been neatly addressed by the Windows Static Driver Verifier. If it can't decide if a driver is memory-safe and API safe in some number of minutes, it quits, and Microsoft won't sign the driver. About 5% of drivers hit this problem, and need some rewriting.
If your kernel driver is anywhere near undecidability in memory safety, it's broken.
What does “undecidable program” even mean? The label “undecidable” applies to decision problems: https://en.wikipedia.org/wiki/Decision_problem
> If it can't decide if a driver is memory-safe and API safe in some number of minutes, it quits, and Microsoft won't sign the driver. About 5% of drivers hit this problem, and need some rewriting.
We're talking about the universe of C++ programs that meet the Core Guidelines, not just drivers.
It's pretty clear what it means in this context, especially after the Halting problem was mentioned earlier in the thread, and the parent wrote about MS driver verification program "deciding whether a driver is memory-safe".
Sorry, no. Imprecise use of jargon is one of the main ways how technical discussions get derailed. So I shall insist on precise use of terminology.
Yes. And the other main way is insistence on pointless pedantic distinctions.
Now, I'm aware that Animats mentioned a concrete static analysis tool, the Windows Static Driver Verifier. But, without any knowledge of the specifics of how it internally works, you can't design your program to pass the verification.
While I have worked on plenty of C++ projects that had sections with performance-critical code or frequently-allocated data structures that need careful control over memory, I have never seen an entire program in this category. For that matter, I probably haven’t seen one with more than 40-50% of the C++ code in this category. Invariably, you start to see people write painful code to do trivial-things-that-are-not-trivial-in-C++, intermixed with the things that might actually benefit from C++.
You can offload a lot of less-critical C++ code, forcing it into a higher-level programming environment where fewer things can go wrong, in a language that is accessible to more programmers.
Ideally it'll lead to something like Terra http://terralang.org/ or Cxx.jl https://github.com/Keno/CXX.jl
But now Haxe http://haxe.org/ has several advantages that's worth consider.
Besides the usual set of using, with, try-finally, there are higher order functions for those languages that support it.
For example,
with-file "/pathname" {
//fd is only visible and open here
}
accept-connection {
// socket
}
// ....
You just need higher order functions and implicit parameters to be able to do that in your GC language of choice.You also need to remember to use RAII in every other case.
You might also need to implement RAII handle classes in specific use cases.
I guess these FP patterns are yet to be widely known.
And then you need to add smart something into the mix.
Which is no different than using resources from scoped pools.
Fixing this is the whole point to static lifetime checks.
> It's perfectly possible to do the same thing in a higher-level language though - if you only expose with-file then you have the same safety guarantees as RAII.
It's still less sophisticated than desirable. In Rust, objects can be moved to a different environment, so cleanup need not follow a strict LIFO discipline.
Python is not very good as an embedded language (globals and locks). Lua is OK but AFAIK making C++-like classes in Lua is a pain, and the language is a bit annoying for other reasons.
As of Windows 8,it is even easier given the new COM ABI model aka WinRT that all languages that wish to be used must support. You just need the ability to use WinRT projects on the language tools.
On mainframes like IBM i, you can take advantage that all languages, including C and C++, compile to a common bytecode to make some wrapper libraries as well.
1-based indexing. Prototypes rather than classes -- I don't think having to roll your own or research an object system for every project is a good use of time.
Lua is a great language implementation, but whenever I try to actually program in it, it feels like an inferior version of Python...
Rather than embedding Python in the C++, write the top-level loop in Python and call into the C++ from there.
- [0]: https://github.com/rustbridge/helix
First thing I did was write up a test program that looks something like this, to see what errors I would get:
int x = 42; int* px = &x; px++; *px = 12;
Compiled on GCC with -Wall. No warnings. I thought for sure some warning must prevent pointer arithmetic. After asking around I guess what I did above wasn't really pointer arithmetic, at least not in the compiler's eyes, so it's not warned against.
In C++, directly using pointers to access arrays is often discouraged, in favour of using the standard collection types and their associated iterator types. So in code that follows this style, it is possible to warn about uses of pointer arithmetic, and the core C++ guidelines do suggest such a warning. But the guidelines also point out that "this rule would generate a huge number of false positives if applied to an older code base": https://github.com/isocpp/CppCoreGuidelines/blob/master/CppC...
http://stackoverflow.com/questions/11714827/how-to-turn-on-l...
https://www.cis.upenn.edu/~eir/papers/2013/ironclad/paper.pd...
https://github.com/crdelozier/ironclad/
Rust still wins out against it. Yet, a C++ subset with type and memory safety capabilities does exist. The benchmarks look good with conservative GC and all checks on. I vote it as worth consideration for anyone trying to improve C++ app's safety. Might be worth FOSS types improving on if they're sticking with C++.
Edit: Removed link to tool tgat wasnt static. My mistake on that.
Take this blog post, replace “Lisp” with “C++”, “Haskell” with “Rust”, and “type system” with “lifetime system”: http://axisofeval.blogspot.com/2011/01/why-lisp-is-big-hack-....
“C++, as it stands, can't do any of that, and won't be able to do any of that. That's simply a fact. Why? Because it's coming at the problem from the wrong direction. Trying to graft an interesting lifetime system or verification onto C++ is simply too heroic and ill-specified a task. C++ starts with ultimate freedom/unsafety, and you can't constrain it. Rust starts with ultimate bondage/safety, and then, piece by piece, adds that freedom back. On a theoretically well-founded basis.”
This summarizes C++'s safety woes.
> 2013 Update: I was young and stupid when I wrote this.
So making the unsafe language safer (even if not completely) can have much bigger impact and to far more people than touting some perfectly safe little language.
Is this a plea for sticking to broken technologies 'cause social/environmental reasons?
Interesting. To my mind it's the appeal to what the author thinks today that's the appeal to authority, because there's no explanation given for that, whereas the argument as written stands on its merits as an argument.
Why C++ will probably not be changed by other languages is the freedom it provides. It is a language equivalent of giving you absolute power of everything, including digging a massive hole, falling into it and not being able to crawl back out.
Yes, Rust is safer in and of itself, and "encourages" safety, but it is also like riding a bike with a safety wheels and cushions strapped everywhere. At some point it will start just getting in your way more than helping you.
In the end it boils down to purity and elegance vs. efficiency and practicality. And I'm bigger fan of efficiency and practicality than purity and elegance.
This doesn't match my experience using Rust, and I've in fact found quite the opposite.
("Need" meaning "it's fundamentally impossible to do it safely", not just "the language provides no way to do this")
On the other end of the scale, Rust doesn't have much annotation overhead, but if you truly want zero cost, the borrow checker has significant limitations - you can't have any cycles in your ownership graph, not even parent pointers. The alternative is to live with reference counting overhead, and I'm not sure how much benchmarking has been done of exactly how much overhead that is, but the community tends to value pragmatism over purity, so a lot of data structure libraries take the third option and use `unsafe` fairly liberally behind the scenes. (Which makes the parent's point somewhat inaccurate - Rust is not pure 100% memory safety; unfastening the seatbelt is possible. You just have to explicitly request it.)
Of course, there is a huge gulf between those two paradigms. There are languages out there that could be said to live somewhere in the middle, like ATS, but they're not popular, and in general I think the spectrum is quite poorly explored. Certainly in theory there should be a fair amount of room to build from Rust's pragmatic approach and improve on it, by expanding what kinds of ownership models can be checked and/or reducing annotation overhead - but so far nobody has figured out what such an improvement could actually look like (except maybe this C++ thing, if it's not vaporware, heh), so it's up in the air how far this can be taken. I expect and hope Rust itself will continue to improve.
* Technically not any, due to Gödel's incompleteness theorem, but certainly any code a human would write. (Anyone who cites either the incompleteness theorem or the halting problem as having practical relevance to basically anything in computing is mistaken.)
Sorry, but it's not true, see Gödel's completeness theorem. It basically states that any "true" statement is provable. So you can verify any correct code as correct. There are problems if you try to verify a not correct code as correct though.
C++ behavior is almost certainly more complex than Peano arithmetic.
It states that every statement that is true in all models of a first-order theory is provable within the theory.
*(unsigned long*)0xF0010010 = 0x11;
to make the hardware do some particular thing.But if that's how you're controlling your hardware, how are you going to do it "safely"? You'd need your compiler to know which addresses are valid and which are not, which means your compiler needs to know about your custom hardware, and your compiler needs to change each time you revise a circuit board. That seems like an approach that is unlikely to be productive.
On the contrary, having the compiler worry about correct resource management frees you to concentrate on other things.
> In the end it boils down to purity and elegance vs. efficiency and practicality.
You'd be surprised. Elegance and efficiency are aligned more often than not.
It's a hindrance when you're trying to prototype things to see if something's even worth while.
I don't see the value in deliberately writing software of non-optimal quality. Bugs can happen to anyone, but I take issue with not aiming for excellence.
Obviously if you write code for a prototype that has several limitations (say it's single-threaded) in order to determine if a new algorithm provides the correct result, you probably don't want to be concerned at this stage with complete safety if you can run it for the prototype in single threaded mode and probably not have any issues.
That might allow you to make a decision as to whether to continue down that path quicker than having to write a more robust solution and then realise it doesn't actually work with certain input data or something.
C++ isn't exactly the first language I would pick to test the viability of an algorithm. Too much ceremony, distracting me from the essence of the algorithm. ML seems more up to the task.
If you want to check its specific runtime behavior and speed with C++, for incorporation into a program, then you can't just use ML or Python etc.
Re-write thousands of lines of C++ infrastructure into another language just to test it in this "theoretical" language?
Or re-use the existing code and write a slightly hacky prototype within the code which can close to effortlessly run any data the program creates/accepts.
I know which one I'd pick.
If I was going to market such a program the next step would be to rewrite this in Java, C++, or Go (even Rust once I learn it).
I'm not interested in marketing a freecell game, so I will rewrite it only for fun, someday. I got started on the program because my wife is twice as fast as I am at that game (and every other game not requiring shooting or math); I thought well at least I'm a better programmer than her (for now, she hasn't tried that yet!) so I'll write a program to play freecell.