What comes to mind when you hear "reliable software"? Personally, I immediately think of critical embedded systems like rocket and spacecraft guidance and automotive control systems. And one thing I hear repeatedly is that many well-known coding standards for building such critical software prohibit all dynamic memory allocation, because dynamic allocation is a potential failure point.
And of course, if you don't have dynamic allocation, you don't need garbage collection, because it would have nothing to do. So if the software that needs reliability the most often doesn't use dynamic allocation, only the most ignorant could think that you need garbage collection for reliable software.
This has been a common meme for the past 15 to 20 years precisely because it is so easy to forget when a block of memory needs to be freed. However, the mechanisms of reference-counted smart pointers, RAII, and clearer ownership semantics in the language, go a long way to help mitigate the common manual memory management problems in C++.
The downside, of course, is that you have to know how to use these ideas to write "reliable software", and C++ does not make it easy. It's pretty much impossible to go from reading the standard to implementing correct and optimal C++ programs. There are so many gotchas, corner-cases, and features which require much study and experience to truly understand.
You're also arguing a straw man. Of course you can write reliable software without dynamic allocation. The question is: can you do it faster and/or cheaper using C++ or $ALTERNATIVE?
(You mentioned rocket and spacecraft guidance software as examples. That's an example of software that's exceedingly expensive to develop... and it doesn't actually do that much even though it's obviously complex.)
If we're going to be thorough about a discussion of reliability in software, we have to include cost in the equation. Rocket and spacecraft systems have extremely high cost per SLOC. It stands to reason that there might be solutions for producing reliable software (for some standard of reliability) far more rapidly and cheaply than the folks at NASA.
As far as cost, you're completely right. My point isn't that we should all be building software this way (far from it, most software doesn't need to be that reliable) but rather that when reliability really matters, garbage collection isn't in the picture, so the "myth" being addressed is stupid.
You're making a distinction where none exists. All software (that we know of, anyway) is the product of humans.
but ultimately the software behaved exactly as it was intended to
I somehow doubt the designers intended for the software to cause the mission to fail.
but rather that when reliability really matters, garbage collection isn't in the picture, so the "myth" being addressed is stupid
I would continue to disagree. I don't accept it on faith that the standards of fallible agencies such as NASA are proven correct, especially given the counterexample I mentioned earlier. There are ways to implement hard real-time garbage collectors and prove them correct with far more rigour than was employed in the MCO mission. The side benefit of such proofs is that we us mere mortal programmers can benefit from the work in our everyday lives, something you can't say about the extremely domain-specific code used by NASA.
Why so upset if I may ask? I think Apple declared GCs "bad" anyway.
Correction, Apple failed to implement a working GC, given the constraints on Objective-C semantics. It was a conservative GC and still it borked when mixing libraries not compiled the same way.
So they made a sensible option of having the compiler insert the retain/release method calls that Objective-C developers would need to do manually.
This only covers framework code or objects that follow Cocoa semantics, everything else is manual.
Swift, being binary compatible with the Objective-C runtime, needs to make use of the same memory model.
Why do you think I'd care what Apple says about garbage collection...?
That was just a joke with Apple. :)
"Only program in C++ if you absolutely have no other option."
I believe in this myth, I also am currently working on a project in C++ and reading one of Strousup's books. I think even Strousup shares this belief, but he would probably phrase it a bit differently. (My project is interfacing with the Unreal Engine by Epic that exposes a C++ API.)Just putting this out there for the discussion's sake, perhaps someone could engage on it. I recently discovered to my horror that Apache Mesos is written in C++. They use the nice futuristic style Strousup would probably applaud though, still I think any other practical programming language would have been a better fit since it's just a sysadmin tool. Obviously they disagree.
I have been writing C++ for over a decade and am currently writing a cross-platform GUI for controlling remote hardware over a network link (multicast). With accelerated graphics for 3D interaction, entirely custom widgets/controls, and interworking with a low-level communication library that my colleague has written, other languages would have been awkward. There is reuse of parts of the library with the remote hardware I think, as that is written in C (isn't all embedded stuff?). The C++ nature of the library will also mean less pain when porting to other platforms.
Higher level languages may be more fashionable but who wants a slow GUI app written in Python and a big runtime to distribute with it? (How I detest all of RedHat's update programs over the last 15 years and Ubuntu's abysmally slow software centre....). I know people like to hate C++ because it's complex compared to BASIC/Pascal/Python/JavaScript/whatever is in fashion now but its flexibility allows construction of complex concepts.
I have also written servers and web servers in C++ and enjoy cross-platform use of them (albeit with some macro hash-defs for compilation); the clients for them are also native GUI apps written in C++. Admittedly many of the applications I have written could have been written in other languages but it's the way I think; the stability of the development platforms is a great attraction for me (contrast this with .NET 2.0/3.5/4.0/5.0 differences and the big runtimes that need to be thrown around everywhere).
I suppose it's the right tool for the job in the most part, and if you're just starting out with C++ then it may be more painful to get something usable and understand what's going on compared to writing a few lines in JavaScript and showing them in your web browser.
But I find it rewarding. Horses for courses I suppose.
Contrasting this to Julia/lighttable/Juno is like night and day. Programming in a canvas/all REPL style is unbelievably freeing since each little piece can be tested and iterated on quickly and easily. Then the program can be organized in a continuous matter while it is being made. The mental energy needed at any one point in time is massively decreased since classes, inheritance, types, memory, and data flow don't all have to be dealt with in an interwoven manner like C++.
The performance won't be equal immediately, but being able to prototype, then optimize, then only have to replace minimal parts with native code after they have been shown to be bottlenecks is amazing (and calling C is even super direct because of strong typing)
So after a solid year, I agree with the idea of using C++ only if there is no other option.
Reading through "Effective Modern C++," I was constantly thinking, "How is anybody supposed to remember all of these corner cases?"
Choice of programming language is an architectural decision with a big impact and it can't come down just to preference or how nice a language feels, although that plays a part too. One can also do quick prototyping in one language and the real thing in another one, although prototypes have a nasty habit of surviving into production.
Back in 2005 two Microsoft developers (Raymond Chen and Mariani) did a blog series where one of them wrote and optimized a C++ Chinese/English dictionary and the other did the same in C#[0]. The naïve C# version outperformed the C++ version until Raymond performed several significant optimizations.
[0]: http://blogs.msdn.com/b/ricom/archive/2005/05/10/416151.aspx
Here's my standard example for when I would happily choose C++: I am writing an image processing library that I want to be able to run on both the PC and embedded platforms (e.g. one of TI's DSPs). I also want to be able to write low-cost bindings in python, ruby, etc. There are other options, but I think C++ is the best one in this case.
D is more mature but still can't compete on tools since only a few languages can make that claim.
Your case is another big point. C++ can manage memory without raw pointers AND without garbage collection. It should be able to compile without a big runtime, but for some reason its modern dependencies are just as big as scripting languages.
http://blog.rust-lang.org/2014/12/12/1.0-Timeline.html
* now -> Jan 9: TONS of breaking changes
* Jan 9 -> Feb 16: probably shouldn't break, but we
reserve the right to deal with exceptional circumstances
* Feb 16 -> six weeks, maybe 12: no more breakageWhat I tend to think people mean when they say that, is that you have some cycles to "waste" on a simpler - while also being high level - language.
> Here's my standard example for when I would happily choose C++:
I think the standard examples are certain application-level programs.
Implicit garbage collection is about the ease of writing code, not reliability. You make the compiler work for you by not making what you want explicit. Yes, for bad programmers, this leads to better code, but that doesn't mean you don't benefit from implicit garbage collection if you are a good programmer.
No, it is about reliability. You are confusing not always necessary/perfect/optimal with average case reliability. On average, automatic memory management has less bugs because certain classes of mistakes can be entirely eliminated.
Also this argument of good programmers don't have to rely on X as a crutch is more about pride than productivity. In the real world, most teams have code touched by developers at a variety of skill sets and everyone's contributions affect quality of code so tools should be measured across levels of expertise.
If you look at the highly cited research on garbage collectors I believe you'll find most of it concurs as to the benefits, edge cases not withstanding.
Not to mention that sometimes even Homer nods.
If something needs to happen in a certain way and you have to manually ensure that it happens every time, sooner or later even the best of us will slip up. Now, the tradeoff in power and flexibility might be worth the risk of shooting ourselves in the foot, but it's still a tradeoff.
C++ offers the most flexibility, and using containers and smart pointers makes code that doesn't care much about memory look quite decent too. I have to admit that Swift makes it a breeze though, you don't have as much overhead in thinking about memory management.
The problem is that you spend most of the time dealing with issues that are created by the language, not from the problem domain.
Every minute you are debating which kind of smart pointer to use, interpreting error messages (which are quite verbose), writing copy constructors and assignment operators, or forgetting to add a virtual destructor, is a minute not working on your problem domain.
If it is performance you're after, then C++ is also bad in the sense that it's very difficult to reason about. Due to optimizing compilers, that's true to C, but the abstraction is way thinner.
Garbage collection handles 90% of the problem transparently to the programmer. That's wonderful (really - I'm not being sarcastic here). But the problem is, garbage collecting languages usually don't have destructors, and so you're left having to manage the other 10% of the problem yourself. You have to remember to close all your own files, clean up all your database connections, and so on. Garbage collection usually makes the 90% trivial, at the price of giving you no tool at all for dealing with the other 10% (non-memory portion) of resource management.
In languages that use lambdas/closures, you can make some sort of RAII as well.
It is a well known pattern in Lisp and ML languages.
Before those were introduced we had try/finally which works equally well but is slightly more verbose.
Are there some good stories of the bad of C++ that anyone can share?
Languages that contain lists and maps as first class types and the syntax to go with it are so much nicer.
It's no joke that large C++ applications contain a poorly defined subset of common lisp.
The standard library is small. Things that are included out of the box in other languages do not. Doing some simple things are not as simple as in some other languages.
When dealing with math, on the other hand, as systems languages go, C++ is not the worst alternative. Getting numeric stuff correct is about as difficult in all languages.
I would not use C++ anything that benefits from prototyping. Otoh, when the spec has been defined and it is fairly obvious what needs done there is no reason why it should not be banged together using C++.
std::vector<std::string> exampleparser::tokenize(std::string some_input){
That is unless I'm doing it wrong.And of course, his books (More) Exceptional C++ (http://www.gotw.ca/publications/xc++.htm, http://www.gotw.ca/publications/mxc++.htm) also contain nice examples.
The basic problem with C++ is that it's hard to tell if something really bad is happening. Think of C++ from the view of the maintenance programmer assigned to figure out why a C++ program is crashing intermittently. Or worse, the security expert trying to figure out how a system was penetrated. C++ has hiding ("abstraction") without safety. This is a bad combination, one seen in few other major languages.
We make progress in programming partly by having the language eliminate some problem. High-level languages mean you don't have to worry about register allocation, saving and restoring registers, or calling sequence conventions. Assembler programmers have to obsess on those issues. C++ doesn't eliminate any problems from C. It helps with some of them, but not to the point that they're just gone.
The three big questions in C are "How big is it?", "Who owns it?", and "Who locks it?". C++ helps a lot with the first one, even though it doesn't have enough subscript checking to guarantee the absence of buffer overflows.
C++ has struggled with the second one, with three rounds of auto_ptr, unique_ptr, and now reference-counted smart pointers. But because these are an afterthought, implemented using templates, they're neither optimized nor airtight. The mold keeps seeping through the wallpaper, in the form of raw pointers. Rust has a designed-in solution to this problem. (Rust's borrow checker is perhaps where C++ should have gone, but C++ will never get there.)
C++ as a language has no clue about who locks what. The language totally ignores concurrency. That's said to be an operating system problem. This is now a very dated concept.
(As I say occasionally, I really hope the Rust crowd doesn't screw up. They address all three of those big questions in effective ways. But I see too much use of "unsafe" in Rust code, which indicates weaknesses in the language design. The use of Rust's "unsafe" for "performance" is a big problem. From a semantic standpoint, only "Vec" needs "unsafe", because somebody has to convert raw memory to an array of objects. Everything else can be built on top of "Vec". But there is unsafe code for "performance" in hash classes and such.)
Mixing smart pointers and multi-threading in C++ may result in some nasty surprises that are very difficult to track down and would be impossible with garbage collection, and I'd be curious if Rust would be able to do better here with its fancy type system.
Additionally, raw pointers are not dangerous. If you're passing them around as const pointers, the receiver can't do things like deleting them. If you are writing a container class (instead of using one of the existing myriad of containers in the STL) then you can use raw pointers. But typically you would not need to write your own container class; just use one of the STL's and the move semantics. You can then use references everywhere instead. Or const references. Everyone forgets about const correctness in their C++ bashing.
For concurrency, you may wish to see http://en.cppreference.com/w/cpp/thread
There's even mutexes in there.
Objects hide their implementation details. An object is only a valid abstraction if it correctly hides its implementation details and the user can ignore them. If there are hidden constraints on what a user can do with an object, but those are not enforced by the object, the object is an unsuccessful abstraction and a potential source of bugs.
Additionally, raw pointers are not dangerous.
Two words: "buffer overflow". C pointers lose size information.
#include <iostream>
struct Foo {
const int &dangling;
Foo(const int &dangling): dangling(dangling) { }
};
Foo foo() {
int dangling = 0;
Foo foo(dangling);
return foo;
}
int main() {
std::cout << foo().dangling << std::endl;
return 0;
}
Neither g++ nor clang warn on this, either. I am not sure why you think const pointers are safe. They aren't. Flat out.