
The C++ bashing season is back (2009) - luu
http://eli.thegreenplace.net/2009/10/17/the-c-bashing-season-is-back
======
Animats
Now is a good time to bash C++. The alternatives are better than they were in
2009. For web stuff, Go is a good alternative. Rust is starting to look like a
good alternative for low-level code.

The C++ crowd keeps trying to paper over the Mess Inside with templates, but
the mold keeps coming through the wallpaper. After dealing with C++ since the
mid-1990s, it's time to move on.

There have been good alternatives over the years, but their backing companies
failed for other reasons. Xerox had Mesa and Cedar. DEC had Modula 3. Borland
had Delphi. None went mainstream outside their niches.

Today, a compiler almost has negative commercial value. It took $20 million to
launch Java, and the compiler was given away. The Go and Rust compilers are
free. There was a time when a C or C++ compiler cost several hundred dollars.
Now, you have to give them away. It's hard to fund development in that
environment.

~~~
tdees40
I'd make the argument that Rust is making many of the same mistakes as C++.
It's got a kitchen-sink approach that has already accumulated some barnacles,
and that ownership system is a mess.

~~~
Animats
The ownership system, the big innovation in Rust, isn't bad. The type system,
macro system, and error handling interact to form a big mess. Exceptions would
have been simpler than the mechanisms used to avoid exceptions.

~~~
Animats
A note on exceptions. Exceptions have a bad reputation in some quarters.
They're not in Go, Rust, or C, and they're troublesome in Java and C++.
They're successful in Python. Why is this?

It took several go-rounds to make Python exceptions generally useful.
Originally, exceptions were just strings. Exceptions became more useful when
an standard hierarchy of exceptions was defined and libraries were modified to
use it. The Python exception hierarchy has a root of Exception, and one of its
subclasses is EnvironmentError. Errors that reflect problems external to the
program, such as I/O, network, and external data format errors, are subclasses
of EnvironmentError. So if you catch EnvironmentError and report it properly,
that covers most common cases. Exceptions not under EnvironmentError generally
indicate program bugs, so those are usually allowed to propagate upward to the
point where crash-level errors are handled. The default is a program abort and
a stack backtrace.

The other problem is getting things closed out properly after an exception.
C++ tried doing this by having destructors close things. This works until
something goes wrong in a destructor. Exceptions raised within destructors
tend to cause problems. This tends to result in ignoring errors at close time.
Go's "defer" has the same problem. Calling destructors from a garbage
collector is even worse. Microsoft tried to address this in Microsoft Managed
C++, resulting in painfully complicated destructor semantics. (Look up "re-
animation" for Managed C++, if you like.)

The Python solution was a "with" clause. This is borrowed from Common LISP's
"(with-open-file ..) construct. The Python syntax is "with _opening
expression_ as _object handle_ :". The opened object is open for the scope of
the with clause, and upon exit from the with clause, the "__exit__" method of
the object will be called, no matter how the with clause is executed. To make
this work, I/O, locks, and database connectors had to be retrofitted with
"__exit__" methods. This was done around Python 2.5-2.6; it took a while.

The result is that Python functions that open and close things tend to have
this pattern:

    
    
        def dosomething(args) :
            try:  
                with *thingtoopen* as *handle1* :
                    with *otherthingtoopen* as *handle2* :
                        *do work on open things*
            except EnvironmentError as message :
                *report errors which occurred opening things, working on them, or closing them*
    

This is reasonably simple, and tends to result in programs that do something
reasonable for error conditions. Importantly, even if an exception is raised,
the close operations take place. Even the case of a close operation raising an
exception is properly handled; the other close operations still take place,
but they are aware there's an exception situation. A database connector would
do a ROLLBACK instead of a COMMIT in such a situation.

This is probably the direction Rust should have taken. Error handling in Rust
requires excessive boilerplate code every place an error can occur. The new
Rust error hierarchy scheme [1] is especially verbose.

[1] [http://lucumr.pocoo.org/2014/10/16/on-error-
handling/](http://lucumr.pocoo.org/2014/10/16/on-error-handling/)

~~~
Jweb_Guru
Python's with has no advantages at all over destructors that I'm aware of (it
does have some disadvantages, since it's easy to forget) and I'm curious to
ask what you think they are. This may help those of us who are extremely happy
with Rust's error handling story better understand your complaints. You keep
talking about "excessive boilerplate" and the like, but that simply isn't what
I observe in practice--it could certainly be better, but existing solutions
are fairly unobtrusive. You also claim that exceptions are "successful" in
Python, but I have had just as many issues with exceptions in Python as I have
in Java (if not more, because the exceptions are not part of the function
signature).

The problems with exceptions are well-documented (they require the programmer
to write all his or her code transactionally, leading to so-called exception
safety issues). Subclassing doesn't address the major issues of error handling
at all, as far as I know, but again--I'd be interested to hear how you feel
they are different (Rust already has an IOError that suffices for the cases
you seem to be concerned about, but in practice there are many more types of
error than that).

~~~
Animats
Hacker News isn't really a good place to post code, but here's an excerpt of
the boilerplate required in Rust code that reads XML to pass I/O, HTTP, and
XML parsing errors upward. This, according to the reference above on the new
style of error handling, is now the official right way to do it.[1] Works
fine. You get to repeat all this each time you define a new result type with
possible lower level errors. What's wrong with this picture?

    
    
      //
      //  Return type and its error handling
      //
      pub type FeedResult<T> = Result<T, FeedError>;
    
      /// A set of errors that can occur handling RSS or Atom feeds.
      #[derive(Debug, PartialEq, Clone)] // crank out default functions
      pub enum FeedError {
          /// Error detected at the I/O level
          FeedIoError(IoError),
          /// Error detected at the HTTP level
          FeedHTTPError(hyper::HttpError),
          /// Error detected at the XML parsing level
          FeedXMLParseError(xml::BuilderError),
          /// XML, but feed type not recognized,
          FeedUnknownFeedTypeError,
          /// Required feed field missing or invalid
          FeedFieldError(String),
          /// Got an HTML page instead of an XML page
          FeedWasHTMLError(String)
      }
      //
      //  Encapsulate errors from each of the lower level error types
      //
      impl error::FromError<hyper::HttpError> for FeedError {
          fn from_error(err: hyper::HttpError) -> FeedError {
              FeedError::FeedHTTPError(err)
          }
      }
      impl error::FromError<old_io::IoError> for FeedError {
          fn from_error(err: IoError) -> FeedError {
              FeedError::FeedIoError(err)
          }
      } 
      impl error::FromError<xml::BuilderError> for FeedError {
          fn from_error(err: xml::BuilderError) -> FeedError {
              FeedError::FeedXMLParseError(err)
          }
      }
    
      impl fmt::Display for FeedError {
          fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
              match self {
                  &FeedError::FeedIoError(ref xerr) => xerr.fmt(f),   // I/O error
                  &FeedError::FeedHTTPError(ref xerr) => xerr.fmt(f), // HTTP error
                  &FeedError::FeedXMLParseError(ref xerr) => xerr.fmt(f), // XML parse error
                  &FeedError::FeedUnknownFeedTypeError => write!(f, "Unknown feed type."),
                  &FeedError::FeedFieldError(ref s) => write!(f, "Required field \"{}\" missing from RSS/Atom feed.", s),
                  &FeedError::FeedWasHTMLError(ref s) => write!(f, "Expected an RSS/ATOM feed but received a web page \"{}\".", s)
                  //_(ref xerr) => xerr.fmt(f) // would be convenient, but not allowed in Rust.
              }
          }
      }
    

[1] [http://lucumr.pocoo.org/2014/11/6/error-handling-in-
rust/](http://lucumr.pocoo.org/2014/11/6/error-handling-in-rust/)

~~~
Jweb_Guru
You don't have to do any of this. You can just use Box<Error> in your Result
and handle all types that implement FromError, if you so choose. You can
_also_ decide to do it this way, if you want to have more explicit error
messages. Or you can simply forgo all this and return errors explicitly.

I'm frankly not sure why you think this is boilerplate, though: what, indeed,
is wrong with this picture? Maybe I'm simply missing the obvious, but this
doesn't even seem like that much code to me, especially given that each of
your error types would have to be classes in other languages. (BTW, you don't
need to prefix all the errors with Feed because they are already namespaced).
Once you've written the above, you can simply try! everywhere and get
automatic conversions--and you use try! a lot more often than you define new
result types.

Your example also looks very much like what I would write in a language like
Java, where exceptions are actually part of the signature (which is the
approach Rust takes). Perhaps you are talking to the wrong person here, but I
have never had an issue with Java's approach to checked exceptions, only the
many ways the language provides to undermine them and the difficulty of
creating new error types without ADTs.

In any case, you still haven't addressed my remaining questions re:
exceptions. We may disagree on whether the above is boilerplate, but given
that exceptions have other problems they are still not a drop-in replacement.
I think if you consider boilerplate to be the biggest issue in error handling,
you and I likely fundamentally disagree.

------
api
I'll keep saying it: give me a modern language that is cross platform, results
in reasonably sized code footprints, is fast, and that can be used to ship
code on all the major OSes, and I'll switch. Until then C/C++ is the only way
to achieve that.

It's analogous to the situation with JavaScript on the web. JavaScript is the
language of the web because JavaScript is the language of the web. C/C++ is
the language of systems because C/C++ is the language of systems.

~~~
jayvanguard
Don't mix the two together. C is great for systems and some application
programming, C++ is rubbish.

There was one brief stretch in history around 1994-1998 where C++ was the best
choice for application level programming. And even then only a very narrow
type of app programming.

Before that brief timeframe and since then it has been a poor choice for every
single type of programming. There are better choices in all cases. It has no
niche anymore where it is the best fit, not anywhere.

~~~
CountSessine
_Before that brief timeframe and since then it has been a poor choice for
every single type of programming. There are better choices in all cases._

For memory-sensitive applications where time-amortization hurts (and this
turns out to be pretty much every application that anyone wants to run on
their local computer), C++ is still king and is still the best choice for the
job. Everyone hates C++, and yet every desktop application in widespread use
is built with it. I wonder why? Is it really just inertia?

 _Java, C#_ \- programmers are stuck with a runtime that literally consumes
10x ram as equivalent C++ software. Do you like switching applications on your
Android phone and having every other process pushed out of memory? I didn't
think so. And while both environments are now offering ahead-of-time
compilation, the CLR/JVM runtimes are still much larger than what C++ consumes
and you give up the only chance of matching C++'s runtime CPU performance
(harvesting runtime profiling to inform the JIT).

 _Golang_ \- a huge step in the right direction, this will probably end C++
use on servers and hosts eventually. It still can't match C++'s runtime
performance, but who cares in a host environment. Still, garbage collection
makes it unsuitable for local applications where users notice micro-pauses due
to garbage collection. Nothing is worse than going into alpha with 10 P0 bugs
complaining about 'micro-pauses' and finding out it's because of something you
can't change in the runtime.

 _D_ \- truly a better C++ than C++. But no one uses it.

 _Rust_ \- our best hope so far, but the compiler and standard library aren't
there yet. You still can't program a web server, for example, without manually
handling unsafe objects. Even then its going to be years before there are
libraries available that are of similar quality to things like Qt. How do make
a GUI application in Rust in 2015?

 _C_ \- I hope you like (void*) and all of the fun runtime bugs that
introduces.

But seriously - "There are better choices in all cases"? Like what?

~~~
jayvanguard
> Everyone hates C++, and yet every desktop application in widespread use is
> built with it. I wonder why? Is it really just inertia?

First off, that is incorrect. You underestimate the number of VB, C#, and
other apps, not to mention OSX based apps. Most business apps are either in
Java on the backend or if they aren't web based they are in proprietary
languages and frameworks that likely use a mix of C and C++ at the core. They
are mostly shit nowadays though which is why everyone is re-writing them in
the Cloud. They're unmaintainable.

But yes there are a lot of high visibility popular desktop apps in C++ and
yes, it is just inertia. When were all of those desktop apps you're thinking
of first built (or came of age)? The early 90s! Microsoft Office, the Adobe
suite, Visio (bought out by Microsoft), desktop databases, etc.

That _was_ the age of the desktop and briefly, C++ was the best choice. I'll
grant you a few very small niches still exist and they are in the desktop
domain e.g. web browsers and very complex image editors. But how many people
are writing browsers? And simpler image editors are better off being mostly in
Objective-C or C# (minus some core libraries which are best off in C).

~~~
CountSessine
_Before that brief timeframe and since then it has been a poor choice for
every single type of programming. There are better choices in all cases._

 _I 'll grant you a few very small niches still exist and they are in the
desktop domain_

Show me a popular desktop app written in something other than C++. Even when
developers have tried to write a desktop app in something other than C++ (like
Evernote on Windows) it was deemed a failure and was re-implemented in C++.

And you still haven't presented a compelling argument for choosing C over C++.
C++ IS C, but with extra stuff that makes it better, like generics, custom
types, alternatives to C's nuclear-powered casting, strings that don't blow up
in your face, etc, etc. Why should a core library only be written in C?

~~~
api
"Show me a popular desktop app written in something other than C++."

Eclipse is the only one that comes to mind.

~~~
azth
IntelliJ.

------
xjia
If server-side (web)apps were written in C++ instead of Ruby/Python/..., I
believe they can sustain much more users before they need to consider
horizontal scalability seriously, since we can fully utilize the machine in
C++. AFAICT it is nearly impossible to optimize to the level of cache
efficiency (and etc.) in Ruby/Python/..., and a distributed system is much
harder to cope with, and when you need to work on that, the choice of
programming language becomes less important.

However, building a not-so-bad prototype in Ruby/Python/... is much easier. So
probably something needs to be done (e.g. "frameworks", libraries, tutorials)
for C++.

[EDIT] C++ (templates) can provide abstractions with zero run-time cost. C
doesn't have such abstractions. Ruby/Python/... don't have such low cost.

~~~
jayvanguard
But most (or at least a huge percentage of) server-side web apps are written
in Java. While I don't love Java it is better in every way shape and form for
server-side web apps than C++.

~~~
xjia
That's probably due to the lack of resources (as I mentioned, libraries,
tutorials, etc.) for programming server-side webapps in C++.

However, this could be a chicken and egg problem. People use Java more often
so they produce more resources.

------
beefhash
I wasn't aware the C++ bashing season had ever stopped. Did I miss something?

------
eliben
Mercy, folks, this post will turn 6 years old soon. Let me know if you have
any questions (or just leave a comment).

~~~
mbrubeck
Isn't it already over five years old?

~~~
eliben
Oops, thanks. Math is hard :) Fixed

------
panzagl
Article is from 2009. I was wondering if this was a reaction from the vaguely
positive press C++11 and C++14 were getting lately, but it's not.

------
wyager
> µTorrent is written in C++, and you could never, ever, make it so fast and
> small using any other language.

That is not a reasonable claim.

The article mentions C, but there are a number of languages that could
ostensibly produce a binary of a similar size and efficiency. Rust comes to
mind.

~~~
quonn
True, but at the time of writing, Rust was not available, yet. Can you think
of another language? Much of the excitement for Rust comes because it really
is the first language that tries to beat C++ (and C) at it's own game.

~~~
pjmlp
Delphi, Modula-2, Oberon, Component Pascal?

All strong in the 90's, sadly the market went somewhere else.

Only Ada seems to survive, but in its own area, high integrity computing.

~~~
geoelectric
To be fair, Delphi was pretty hard to get down to that kind of compactness
because of dragging in the VCL as a baseline.

You could technically program without it in standard event loop, but then the
code would have been unfamiliar enough to other Delphi developers to
perpetuate some of the same issues.

~~~
pjmlp
Why unfamiliar? It is how we coded in Turbo Pascal for Windows.

Not everyone was using OWL all the time.

~~~
geoelectric
Delphi picked up more people from VB and Powerbuilder than it did from TPW, I
think.

I got my professional start using Delphi, but never got the impression that
many of my colleagues knew TPW well.

Of the ones that didn't come from a database or 4GL background, most of us had
used BCW for Windows coding and then switched back to Pascal when it became
commercially viable to get a job in it. Borland Pascal didn't exactly have a
ton of play in 1995. So it's true that they would've understood basic Petzold-
style loops, but it would've been a bit surprising in Delphi.

Later, most of the people I knew using Delphi didn't know bare Windows
programming at all, since MFC had fully taken hold for people from that
background.

I was also under the impression that OWL was quite dominant, but I admit that
I only passed through it briefly between Turbo Vision and VCL.

Still, good point!

------
ksk
C++ is a language designed for specific use cases. If you want a systems level
language with extremely low cost abstractions and write high performance, or
low service-latency applications or applications that run on devices where you
care about power consumption, memory utilized, etc, then C++ is one of the
languages that you could consider.

Its pretty bizarre to compare it to python or ruby or perl. Yes, you _should_
use other languages if you _can_. C++ is used when you _cant_.

------
ericfrederich
I'd like a fresh perspective on this taking at least C++11 into account,
perhaps C++14.

~~~
Doji
C++11 and C++14 make the language bigger, which doesn't address the problem of
the language being too big.

------
somerandomone
>But for some kinds of applications it seems that the only viable options are
still C and C++ ... and you could never, ever, make it so fast and small using
any other language. C++ and C are the only options you have here.

Is Go a viable alternative now?

~~~
rjammala
I think once the garbage collector improves with the 1.5 release, it will be a
serious competitor.

~~~
voidlogic
Most Go apps can get better than advanced Java GC level allocation-memory
performance simply by applying "sync.Pool"
([http://golang.org/pkg/sync/#Pool](http://golang.org/pkg/sync/#Pool)) on
applicable pain points.

Its also worth mentioning using a custom allocator + mmap is possible in Go if
you really needed it.

Go is also better than Java about using stack allocation for objects.

~~~
batbomb
I'd like to see some research to backup your first claim if you can point me
to some.

~~~
voidlogic
You want evidence that a object pool with thread-local caching is faster than
a garbage collector?

Barring major implementation mistakes this should be self-evident, but you
could easily write some benchmarks to verify. Its worth noting that if the
Java GC was good enough people would not bother to write object pools in Java-
but they do. And Go has a very advanced GC/mutliprocessor aware object pool
implementation built in its stdlib.

In an typical object pool only allocation up to the peak concurrent demand for
the given object type will ever be allocated and the kicker is that allocation
will only happen once. Adding thread-local caching means that that will scale
well on multiprocessor systems.

TL;DR: A system that does not generate garbage is going to be faster than a
system that does. My argument is that effective use of a good object pool
implementation in Go is faster than even a more advanced garbage collector
would be.

------
jkot
I am former Java maintenance programmer and I hate C++ with passion for over
decade.

But I recently started KDevelop IDE and was very surprised how nice is C++
with QT lib. I am even considering to join an C++ opensource project.

~~~
nicolasp
You should give QtCreator [1] a try. It's made by the Qt people for Qt
development, but even for non-Qt stuff it's a pretty good IDE.

[1] [http://qt-project.org/wiki/Category:Tools::QtCreator](http://qt-
project.org/wiki/Category:Tools::QtCreator)

------
nether
Will we ever see a AAA video game title written in anything else?

~~~
kibwen
It depends on how you define "AAA". Any big-name game written before the mid-
to-late-90s (Mario, Tetris, etc.) wouldn't have been written in C++. Mobile
games (Angry Birds, Candy Crush, etc.) cannot be written in C++ by dint of
their platforms. Minecraft, a contender for the best-selling game of all time,
is written in Java.

~~~
FigBug
Angry Birds most likely was written (at least partially) in C++ as Rovio has
confirmed that they used Box2D, a C++ physics engine, as the engine for Angry
Birds.

All the major mobile platforms support C++. Android through the use of the
NDK. Windows phone has support C++ since version 8. iOS has supported C++ as
long as there has been the app store, either through Objective-C++ or C++
libraries with a C interface.

~~~
pjmlp
Sadly Android supports C++ just for libraries, any attempt to write a serious
app in the NDK instead of a game, lands you in JNI wrapping land.

~~~
FigBug
I'm doing it now for my first Android app and the JNI isn't as bad as I
expected. I'm using juce and it has a bunch of macros that reduce the JNI
calls down to 1 line, and there are a classes to convert the types. The UI is
all in a webview anyway. It took me under two weeks to learn the Android tools
and get a pretty significant app ported.

But I was horrified at how bad the tools and emulator are. I spent more time
trying to get things to work rather than porting code.

------
a8da6b0c91d
I think people miss the context and subtleties of the Linus Torvalds rants. He
was addressing rather idiotic junk on the mailing list about using C++ in the
Linux kernel. He was trying to shut down some spergs who wouldn't grasp a more
subtle request to not barge in on a decade plus old team of C hackers with
tooling discussions.

In other forums he has praised Qt and uses it.

