Hacker News new | ask | show | jobs
by astral303 3003 days ago
In C++, you don’t pay for features you dont use. Linux kernel has virtual calls for file systems via function pointers. That’s polymorphism. Why not let the compiler handle that?
2 comments

You misunderstood on the meta level. My point was that you want to use one feature of C++, you enable C++ to use this feature. Developer B wants to use ten other features. Developers C..Z want to use different features. Every feature by itself makes some sense in its context but in the end you're using so many different C++ features that no single developer knows them all. If you use modern C++ there are some ugly edge cases you have no idea they exist and if you use older C++ styles there is hell waiting for you.

The hard part is to select which features of C++ to use and more importantly which NOT to use and to ENFORCE these rules.

> The hard part is to select which features of C++ to use and more importantly which NOT to use and to ENFORCE these rules.

Maybe I've just not kept up properly, but I find it surprising that there isn't a tool which can enforce this already. It seems so obviously desirable.

I guess clang-tidy + custom "checks" + CI server enforcement could achieve something similar, but I'm just surprised that it doesn't exist.

(Of course, many of the useful metaprogramming libraries for C++, e.g. Boost::Hana, use a lot of weird features of C++ to achieve their goal of being simpler to use for clients of the library. I suppose it might be possible to limit the warnings to "self-implemented" functionality, but it might be tricky...)

C natively supports type-safe function pointers -- there is no ugly hacking or boilerplate involved in that. I.e., the C compiler handles that. So what's the advantage of wrapping a class around it?
No it doesn't. Or yes, it does, but we're talking about virtual method calls, emulated with function pointers. This is an argument people always make here in the kernel C++ debates or the GNOME vs KDE debates (GNOME is C and GTK, KDE is C++ with Qt)

What needs to be happening, to avoid crashes and get correct functioning:

1) virtual method tables need to be allocated (otherwise you might be getting a function pointer from unallocated memory and calling it. Good luck surviving that. Bugs like this have happened in both kernel and GNOME).

1b) Pointers to the virtual method tables need to be correctly set upon every allocation of the struct that contains them. So you lose the ability to create an instance without constructing it. (again plenty of bugs)

2) every level of the inheritance hierarchy need to fill in the function pointers in the correct order (You can't count the number of bugs of this type in GNOME).

3) the pointers for same functions need to be at the same memory location for every object (and the corollary. Function pointers for different functions cannot, at any point of the inheritance tree, have the same location). This leads directly to the kernel datastructures, where everybody is utterly terrified of changing anything or even reordering fields. And sadly, that fear is there for good reason.

4) 1, 2 and 3 need to be redone (best from scratch) any time the inheritance hierarchy changes. And of course, need to be done correctly, so you really ought to erase the whole thing in the whole inheritance (and somehow tell out-of-tree developers to do the changes on their end), but in C nobody does this because of the amount of work involved. Then ... bugs happen.

5) May God help you if you modprobe a module across one of these changes without recompiling it. In other words, any change to the inheritance hierarchy risks making out-of-tree modules deathtraps (such changes require modifying the source to the out-of-tree module and recompiling)

In C++

a) put "virtual" in front of the function you want to work across the inheritance hierarchy

b) (optional) don't remove or reorder virtual functions in versions where you want to maintain binary compatibility (caveat: not in the functions themselves, and of course, not in any data structures they might use) (in short: anything out-of-tree needs to be recompiled)

Given that KDE maintains binary compatibility across major versions b) is not optional within that project (with a bit of a cleanup at avery major version). Except for DCOP. But if you recompile from scratch for every deployment like every large C++ shop, you can just outright ignore it.

The result of this is that the lookup tables can be compiled into programs. This works ridiculously fast. A virtual function call in C++ is one indirection. Not even one extra instruction (just a much more expensive one than the one you'd ordinarily use).

In Java, if I remember the last time I checked it was ~20.000 instructions (but can be compiled out by the JIT compiler ... eventually. Then it's still ~1000). And I assure you Java is a lot more efficient at this than any scripting language.

This is the usual difference between C and C++. In C++ you get all the advantages that lots of manual work gets you in C, at the same runtime cost.

The criticism is that if you give inexperienced programmers lots of high level tools, they will quickly use it to blindly generate programs that are 20G+ of machine code. And ... well that's true. Fixing it can be pretty hard.

The arguments of people like Linus are essentially that it's a good thing that people go through and redo the low-level stuff regularly. It's a lot of work but at times you find problems and inefficiencies. I do agree with that, but sadly, I find it pretty hard to assemble a 2K+ member team that I don't have to pay. So work that programmers don't have to do is a win for me.