"The split between source and headers, which makes project management quite slow. If you make all header libraries you’ll have a lot of copied code – the binaries will be larger. You’ll have to recompile that code every time you want to use it."
You can't say "the split between source and header sucks" just because it's painfull to use it wrong ("all header libraries") !
(All header libraries are justified when you're dealing with highly templated stuff, but in this case you have no way to avoid recompiling this code every time you use it, as each required template instance needs to be generated anyway - smart compilers will get rid of duplicated instanciations).
The split between source and headers is a key to good encapsulation. From a software design point of view, it's good way to break long dependency chains.
I agree with your sentiment, but I think the situation is a bit more nuanced.
The usual argument is that headers don't actually provide good encapsulation: implementation details internal to the class (i.e. private members) end up getting leaked into the public header. There are workarounds (pimpl, opaque "handle" types, etc.) but they all have their own disadvantages (mostly an extra layer of indirection).
Complicating the matter is the issue of static polymorphism--CRTP and its friends cause the template-ization forcing lots of code into headers. Smarter compilers help a bit (with de-virtualization and constexpr), but if you want to guarantee that something is resolved at compile time, turning it into a template is the only real option.
Lastly there's the inlining specter; the separate compilation model means that without link-time optimization (which has admittedly made great strides in the last few years), code must be moved into headers to be eligible for inlining. Premature optimization and all that, but this is a death-by-a-thousand-cuts situation where the language gets in the way of doing the performant thing (and you're probably only using C++ if you care about performance in one aspect or another).
None of these things is an insurmountable problem, to be sure, but they represent little inefficiencies (either for the programmer or the program) which are not generally present in more modern language implementations. I am thinking primarily of Rust, for which the static polymorphism and link time optimization stories are strong--although perhaps that is in direct reaction to some of these C++ shortcomings, and we'll discover Rust has warts of its own after a few decades of wear and tear.
Call me old-fashioned, but I actually think the separate compilation model is a good thing: it makes it possible to delegate the build process to a mostly language agnostic tool, and to mix languages easily (C, C++, D) in the same project.
About the implementation details internal to the class: the 'private' keyword is the problem, not the header files.
If you want polymorphism, why not just use an abstract base class - as you would have had a layer of indirection anyway.
If you don't want polymorphism, just use "handle" types. Am I missing something?
Spoken like a true c++ fanatic. Good encapsulation does not require splitting things up in different files that are later conditionally merged back together by a dumb non language aware text pre processor. Other languages manages this fine without it. And "Template instantiation"? Thats an implementation detail I don't want to think about.
C++ does have its nice parts and it does have its uses but some parts truly are shit, get out of the Stockholm syndrome and admit it.
Let's get this straight: I'm not advocating for the preprocessor here. I will happily put it in the trashbin where it belongs as soon as I have support for modules in C++ (By the way, 90% of the code I write is written in the D programming language, which has modules, and no preprocessor).
I'm advocating for interface files.
Whatever mechanism you use, you still need a way to separate interface files from implementation files. Interface files can be header files, they can be java files containing a single java 'interface', they can be "D interface" files (.di). It doesn't matter which mechanism you use for this, as long as you have a way for your caller not to depend on the implementation of callee (which might, for example, directly depend on a specific audio API, for example).
My point is, if you want the caller to be properly isolated from the implementation of the callee, having to maintain signatures in an interface file is unavoidable.
Call me a barbarian, but I like having the API separate from the implementation. OK there are some 'private: struct MyImpl* _impl;' things in headers, and yes templates (but you can put those in a .inl file and be on your merry way...) but overall - I much prefer .h/.cpp over .java or .cs.
Sure other languages 'manage' it fine. And if you want, you can put everything in your headers in C++, there's hardly any 'need' for .cpp files. Of course it will blow up your compile times, especially without precompiled headers (which are a pita with gcc, and relatively recent there too - gcc only started supporting them in 2006).
You don't have to split, though. You can put all your code in the header file. Your compile times will suck mightily, but you can do it, if managing headers is such a burden.
Can't agree more. IMHO, this Stockholm syndrome / religious cultist mentality is so horrible, the most horrible thing about C++..
Don't get me wrong, I can forgive all shortsighted design choices and all the bullshit that piled up over the years. Nothing is perfect, true. But what C++ does to otherwise intelligent people.. This technological enslavement is just fascinating.
I think most people view headers as a mistake. You can use it to separate the interfaces as you mention, but there are limits to how much you can do that, and parsing the headers over and over slows the build down to no real benefit.
If you look at the modules proposal Microsoft has been pushing for, it provides what looks like a better approach. You won't get the build slowness, you can explicitly control what gets exported.
>The split between source and headers is a key to good encapsulation. From a software design point of view, it's good way to break long dependency chains.
Yes, I'm currently working with Swift and I'm really missing header files. I just can't figure out how to get a quick overview of say a class' public interface without having to scroll through pages of implementation code.
Header files can be a real pain. But they are useful.
You can't say "the split between source and header sucks" just because it's painfull to use it wrong ("all header libraries") !
(All header libraries are justified when you're dealing with highly templated stuff, but in this case you have no way to avoid recompiling this code every time you use it, as each required template instance needs to be generated anyway - smart compilers will get rid of duplicated instanciations).
The split between source and headers is a key to good encapsulation. From a software design point of view, it's good way to break long dependency chains.