4 ms·
The alternative is asking dependencies to never ever make any breaking changes anywhere. This is not sustainable long term. It forces library authors to grow A
by pornel 6y ago
The alternative is asking dependencies to never ever make any breaking changes anywhere.
This is not sustainable long term. It forces library authors to grow APIs purely additively, which eventually makes them overcomplicated and full of traps that can't be fixed, e.g. look at X11 and its recurring drama of screensaver lock security.
Library authors have to eventually clean stuff up. Ossified applications have to eventually update. Semver just adds a way to coordinate it.
- xorcist 6y agoThis argument is sensible, but given that applications outlive both hardware and operating systems, the alternative is users keeping whole operating systems around forever without updating them. The Linux kernel is one opposite example. They are pretty hard line about never breaking userspace APIs. It is overcomplicated and full of traps though. For most end users it's still worth it. The reason you took X11 as an example is because it's still around, all modernized APIs that leveraged broken backwards compatibility are long gone by now.
- slaymaker1907 6y agoThere is a solution to the additive API problem, but C and C++ do not allow it. The solution is to create separate modules like x11/v1, x11/v2, etc. which live in the same distribution. It is therefore permitted for module A to use x11/v1, module B to use x11/v2, and module C to use both A and B (you may need to also provide a shim between common types like a to_v1 function). In this setup x11_v2 probably exposes many of the same bindings as v1, but that is ok since it is low effort to wrap a function or to re-export a private module with the common bindings. This doesn't work in C/C++ due to how the C ABI works. There needs to be an easy way to say that even though A and B both have a foo function they are not the same foo and a way for module C to use both versions of foo by renaming one of them in its source. This is the greatest sin of the C ABI in my opinion, not even the lack of overloads.
- captainmuon 6y agoI think if you use dynamic linking, it is in principle possible for module A to use v1 internally, and for module C to use v2, and neither to know about the other. But you can't use global objects, and you can't share objects from the internal libraries between modules. And the dynamic linker on Linux at least tries to reuse symbols, so it doesn't work by default. This was a big problem in the Gtk2 / Gtk3 transition, where you could not use both in the same process. I think there is a flag to prevent symbol reuse, though.
- account42 6y agoThis is a solved problem with symbol versions and symbol aliases, which are used extensively by libaries like glibc that care about providing a stable ABI. For C++, GCC and Clang also provide the abi_tag attribute: > The abi_tag attribute can be applied to a function, variable, or class declaration. It modifies the mangled name of the entity to incorporate the tag name, in order to distinguish the function or class from an earlier version with a different ABI; perhaps the class has changed size, or the function has a different return type that is not encoded in the mangled name. This was introduced to allow mixing pre-C++11 and post-C++11 std::string and std::list implementations in one libstdc++.so but is more general. The other solution is of course a new SOVERSION but that does not allow two different versions to be used in the same process as ELF symbols are a process-global namespace.