I was prompted to write this by a toot thread that said:
I wonder if C++ programmers know that Bjarne thinks C++ sucks
I'm not C++ programmer, But I remember reading that he said it sucks in some interview. 🤣 It's funny, especially knowing how fanatic the people that program in it and love are about it.
So my thesis is that C++ sucks, but all other languages suck, and for some uses C++ suck the least. Every language has its niche where it sucks the least.
So let's start with stating the obvious, C++ is a horrible language, to have any chance of using it you need to have strong background in C++ to know how to carve C++ into your own C++ dialect that is non-horrible (and yes there is a recursive relationship that basically says that if you have no prior C++ experience you have exactly 0% chance of being able to teach yourself non-horrible C++ subset). So C++ really makes sense in, large, established organisations with strong code quality culture.
When does C++ make sense IMHO:
When you need the performance, so either if your problem can't be efficently coded in e.g. Python, or if you are in an org that has enough scale so it makes sense for them to trade developer time for machine time. Either that or you are doing high frequency trading.
When you really can't have (real) garbage collection. Garbage collectors are fine and dandy, but they lead to unpredictable stop-the-world pauses, and also unpredictable CPU performance, you can have very nice application that uses 1 CPU, and then during garbage collection uses four times this amount;
Or (again Java) you don't like to declare maximal heap size upfront;
When you program things that will run without OS (with minimal OS) and you can pull off C++.
When you dislike the fact that your language has only a single implementation, owned by a single organization;
What I like about C++:
It is performant by default; you can write Go, Java and sometimes even Python code that has the same order of magnitude of performance as C++, but you need to think about it. Especially in Python you need to think about how performant your code is.
I really appreciate that simplest code is the usually performant, that simple for loop will be fast (to understand that you really need to have some Python experience).
Or to put it in other words: "Half baked naive implementation of something in C++ is usually faster than, handcrafted, honed and polished super-fast, state-of-the-art implementation of the same thing in other languages".
You have all the power you need, and the language itself does not constrain you in any way. You have working unsigned integer arithmetics (shame on you Java), you can call low-level kernel API's out of the box;
What I dislike about C++:
- Every other line of code results in undefined behaviour;
- You really need to run your tests twice, one time in debug mode, one time when compiled with all the optimizations;
- Compile times, especially when you use matrix libraries and/or boost;
- The fact that 99% of design patterns are tactical i.e. Java has structural patterns e.g. "Strategy pattern", C++ has low level patterns, like: "here is how to safely make a singleton". Obviously C++ programmers can use patterns from Java (and they do), but somehow most advice you get is not: "this structure will not be extensible" but rather: "here you should have used a const referecne".
Python is a great language, until you hit a wall, and then it's pure hell (frankly: in 99% of software you don't hit these walls so it is okayish).
So you know, Python is great for these 90% of project that don't explode when you know you won't hit the wall. If you hit the wall and still try to use Python then you risk madness.
So these walls are:
Python has no usable multithreading
I know the mantra is: "In Python multithreading is multiprocessing", and this works for a time.
With multithreading you get to share memory, with multiprocessing each process has separate memory (in a naive program) your memory footprint increases N-fold, you can do smart tricks like forking after you lxoaded your code and data in the process space, and this will help.
Multiprocessing in Python is easy, until you care about performance, it's very easy to make parralel multiprocessed code, that will use all of your cores, but spend most of the time copying data.
In Linux each child-process has a Copy-On-Write view of the address space, so you can share memory of the parent process, until you write to that memory, then pages are transparently copied. Bad luck that Python reference-counting garbage collector changes every read to a write (to increase the reference count!).
You can work around that in a multiple of ways, but the problem remains: you need to think about performance.
Python is slow-by-default
Python interpreter is dumb and slow. This is a fact. In Python you write performance sensitive parts of the code in C, or you use code other people wrote in C: namely numpy, pandas and friends.
The above are great, until you get a problem that does not easily yield to vectorization (a problem that is not easily translatable to a sequence of few matrix operations). The problem might be translateable to some arcane incantation of numpy/pandas calls that will be fast enough, but then this only proves the point of "slow by default".
You can try pypy which is a reasonably compliant Python implementation with just-in-time compiler, which is usually much faster than CPython. One thing that is slow in pypy are calls to C libraries, especially numpy/pandas.
You can also try Cython, which is a way to write Python-Like code that compiles to fast-ish C. But you see, then you write part of your app in Python, and part of it in: "almost-python but not really".
Python dependency hell
I really dislike Python package managment (truth be said, there is no good package managment tool, and I hate them all).
In Java at least you have maven, and when you use it, things just tend to work.
I started writing in Python when virtualenvs were a new. And they worked, but then you wake up and you have 92 virtualenvs on your disk (yes this is count for me today).
Now we have poetry which is a very nice tool (but then there are some packages that break it --- yes I'm looking at you Nikola).
And you get all the fun of "when buidling your project you download code from 12 domains, from the internet and run it".
You can try to fix it by Docker, and then instead of having 93 virtualenvs you have 670 random images laying in your disk around (yes I checked today). And this turns: "when buidling your project you download code from 12 domains, from the internet and run it" into: "when buidling your project you download code from 12 domains, from the internet and run it in a cgroup" (which arguably is a progress).
This is somewhat unfair, as C++ escapes this hell only by virtue of not having package management (but maybe it is for the best).
In Javascipt it's another pair of shoes, after a year your project will either need to keep using outdated libraries with multiple vulnerabilities, or you can rewrite it from the scratch, cause your favourite framework changed everythining three times last year.
vue.js is really nice, and core software seems to work well, but I had a lot of pain with setting up the tests.
Fast paced developement
- CommonJS --- modules used by nodejs;
- AMD --- used by major library;
- UMD --- unified module definition;
- And apparently two module variants standardized by ecma script;
All these module formats are subtly incompatible with each other, and with libraries. Some frameworks will e.g. assume CommonJS modules and will explode with cryptic errors when you use other one (maybe errors are not cryptic if you do webdevelopement for a day job, but then when I started you could just paste jquery code to a HTML file and it worked).
The point is that the ecosystem moves so fast that it is very hard to actually maintain things without churning most of the time on doing pointless migrations.
Vue.js tries to abstract this problem, templates they provide try to abstract away most of the details. These abstractions work (but then fail miserably when I try to do something even remotely interesting with the package system).