←back to thread

In Defense of C++

(dayvster.com)
185 points todsacerdoti | 2 comments | | HN request time: 0.003s | source
Show context
fouronnes3 ◴[] No.45268145[source]
This is a good article but it only scratches the surface, as is always the case when it comes to C++.

When I made a meme about C++ [1] I was purposeful in choosing the iceberg format. To me it's not quite satisfying to say that C++ is merely complex or vast. A more fitting word would be "arcane", "monumental" or "titanic" (get it?). There's a specific feeling you get when you're trying to understand what the hell is an xvalue, why std::move doesn't move or why std::remove doesn't remove.

The Forest Gump C++ is another meme that captures this feeling very well (not by me) [2].

What it comes down to is developer experience (DX), and C++ has a terrible one. Down to syntax and all the way up to package management a C++ developper feels stuck to a time before they were born. At least we have a lot of time to think about all that while our code compiles. But that might just be the price for all the power it gives you.

[1] https://victorpoughon.github.io/cppiceberg/

[2] https://mikelui.io/img/c++_init_forest.gif

replies(4): >>45268251 #>>45271645 #>>45273605 #>>45277835 #
jandrese ◴[] No.45268251[source]
In Linuxland you at least have pkg-config to help with package management. It's not perfect but neither is any other package management solution.

If I'm writing a small utility or something the Makefile typically looks something like this:

    CC=clang
    PACKAGES=libcurl libturbojpeg
    CFLAGS=-Wall -pedantic --std=gnu17 -g $(shell pkg-config --cflags $(PACKAGES))
    LDLIBS=$(shell pkg-config --libs $(PACKAGES))

    ALL: imagerunner

    imagerunner: imagerunner.o image_decoder.o downloader.o
replies(1): >>45268768 #
duped ◴[] No.45268768[source]
Consider that to do this you must:

- Use a build system like make, you can't just `c++ build`

- Understand that C++ compilers by default have no idea where most things are, you have to tell them exactly where to search

- Use an external tool that's not your build system or compiler to actually inform the compiler what those search paths are

- Oh also understand the compiler doesn't actually output what you want, you also need a linker

- That linker also doesn't know where to find things, so you need the external tool to use it

- Oh and you still have to use a package manager to install those dependencies to work with pkg-config, and it will install them globally. If you want to use it in different projects you better hope you're ok with them all sharing the same version.

Now you can see why things like IDEs became default tools for teaching students how to write C and C++, because there's no "open a text editor and then `c++ build file.cpp` to get output" for anything except hello world examples.

replies(7): >>45269381 #>>45270904 #>>45270987 #>>45271280 #>>45273544 #>>45277859 #>>45343902 #
palata ◴[] No.45269381[source]
I can use pkg-config just fine.

Not sure how relevant the "in order to use a tool, you need to learn how to use the tool".

Or from the other side: not sure what I should think about the quality of the work produced by people who don't want to learn relatively basic skills... it does not take two PhDs to understand how to use pkg-config.

replies(1): >>45270458 #
duped ◴[] No.45270458[source]
I'm just pointing out that one reason devex sucks in C++ is because the fact you need a wide array of tools, that are non portable, and require learning and teaching magic incantations at the command line or in build scripts to work, doesn't foster what one could call a "good" experience.

Frankly the idea that your compiler driver should not be a basic build system, package manager, and linker is an idea best left in the 80s where it belongs.

replies(4): >>45270803 #>>45270961 #>>45272801 #>>45273841 #
1. jchw ◴[] No.45270961[source]
I'm not going to defend the fact that the C++ devex sucks. There are really a lot of reasons for it, some of which can't sensibly be blamed on the language and some of which absolutely can be. (Most of it probably just comes down to the language and tooling being really old and not having changed in some specific fundamental ways.)

However, it's definitely wrong to say that the typical tools are "non-portable". The UNIX-style C++ toolchains work basically anywhere, including Windows, although I admit some of the tools require MSys/Cygwin. You can definitely use GNU Makefiles with pkg-config using MSys2 and have a fine experience. Needless to say, this also works on Linux, macOS, FreeBSD, Solaris, etc. More modern tooling like CMake and Ninja work perfectly fine on Windows and don't need any special environment like Cygwin or MSys, can use your MSVC installation just fine.

I don't really think applying the mantra of Rust package management and build processes to C++ is a good idea. C++'s toolchain is amenable to many things that Rust and Cargo aren't. Instead, it'd be better to talk about why C++ sucks to use, and then try to figure out what steps could be taken to make it suck less. Like:

- Building C++ software is hard. There's no canonical build system, and many build systems are arcane.

This one really might be a tough nut to crack. The issue is that creating yet another system is bound to just cause xkcd 927. As it is, there are many popular ways to build, including GNU Make, GNU Autotools + Make, Meson, CMake, Visual Studio Solutions, etc.

CMake is the most obvious winner right now. It has achieved defacto standard support. It works on basically any operating system, and IDEs like CLion and Visual Studio 2022 have robust support for CMake projects.

Most importantly, building with CMake couldn't be much simpler. It looks like this:

    $ cmake -B .build -S .
    ...
    $ cmake --build .build
    ...
And you have a build in .build. I think this is acceptable. (A one-step build would be simpler, but this is definitely more flexible, I think it is very passable.)

This does require learning CMake, and CMake lists files are definitely a bit ugly and sometimes confusing. Still, they are pretty practical, and rather easy to get started with, so I think it's a clear win. CMake is the "defacto" way to go here.

- Managing dependencies in C++ is hard. Sometimes you want external dependencies, sometimes you want vendored dependencies.

This problem's even worse. CMake helps a little here, because it has really robust mechanisms for finding external dependencies. However, while robust, the mechanism is definitely a bit arcane; it has two modes, the legacy Find scripts mode, and the newer Config mode, and some things like version constraints can have strange and surprising behavior (it differs on a lot of factors!)

But sometimes you don't want to use external dependencies, like on Windows, where it just doesn't make sense. What can do you really do here?

I think the most obvious thing to do is use vcpkg. As the name implies, it's Microsoft's solution to source-level dependencies. Using vcpkg with Visual Studio and CMake is relatively easy, and it can be configured with a couple of JSON files (and there is a simple CLI that you can use to add/remove dependencies, etc.) When you configure your CMake build, your dependencies will be fetched and built appropriately for your targets, and then CMake's find package mechanism can be used just as it is used for external dependencies.

CMake itself is also capable of vendoring projects within itself, and it's absolutely possible to support all three modalities of manual vendoring, vcpkg, and external dependencies. However, for obvious reasons this is generally not advisable. It's really complicated to write CMake scripts that actually work properly in every possible case, and many cases need to be prevented because they won't actually work.

All of that considered, I think the best existing solution here is CMake + vcpkg. When using external dependencies is desired, simply not using vcpkg is sufficient and the external dependencies will be picked up as long as they are installed. This gives an experience much closer to what you'd expect from a modern toolchain, but without limiting you from using external dependencies which is often unavoidable in C++ (especially on Linux.)

- Cross-compiling with C++ is hard.

In my opinion this is mostly not solved by the "defacto" toolchains. :)

It absolutely is possible to solve this. Clang is already better off than most of the other C++ toolchains in that it can handle cross-compiling with selecting cross-compile targets at runtime rather than build time. This avoids the issue in GCC where you need a toolchain built for each target triplet you wish to target, but you still run into the issue of needing libc/etc. for each target.

Both CMake and vcpkg technically do support cross-compilation to some extent, but I think it rarely works without some hacking around in practice, in contrast to something like Go.

If cross-compiling is a priority, the Zig toolchain offers a solution for C/C++ projects that includes both effortless cross-compiling as well as an easy to use build command. It is probably the closest to solving every (toolchain) problem C++ has, at least in theory. However, I think it doesn't really offer much for C/C++ dependencies yet. There were plans to integrate vcpkg for this I think, but I don't know where they went.

If Zig integrates vcpkg deeply, I think it would become the obvious choice for modern C++ projects.

I get that by not having a "standard" solution, C++ remains somewhat of a nightmare for people to get started in, and I've generally been doing very little C++ lately because of this. However I've found that there is actually a reasonable happy path in modern C++ development, and I'd definitely recommend beginners to go down that path if they want to use C++.

replies(1): >>45273889 #
2. palata ◴[] No.45273889[source]
> Using vcpkg [...] When you configure your CMake build, your dependencies will be fetched and built appropriately for your targets, and then CMake's find package mechanism can be used just as it is used for external dependencies.

Yes! I believe this is powerful: if CMake is used properly, it does not have to know where the dependencies come from, it will just "find" them. So they could be installed on the system, or fetched by a package manager like vcpkg or conan, or just built and installed manually somewhere.

> Cross-compiling with C++ is hard.

Just wanted to mention the dockcross project here. I find it very useful (you just build in a docker container that has the toolchain setup for cross-compilation) and it "just works".