This presentation by Marc Mutz at Meeting C++ 2025 delves into the critical topic of Binary Compatibility (BC) for C++ user libraries. The talk outlines the distinctions between Binary Compatibility (BC) and Source Compatibility (SC), explores the motivations for maintaining BC, and defines forward and backward compatibility. Mutz discusses the significant restrictions BC imposes on library development and then introduces patterns and techniques, largely derived from the KDE and Qt projects, to manage these constraints. The presentation concludes by exploring methods to "cheat" the BC rules, enabling earlier API modernization.
Exploring Binary Compatibility in C++ Libraries: Strategies and Pitfalls
0:05 Defining Binary Compatibility (BC): The talk contrasts BC with Source Compatibility (SC), emphasizing BC's role in ensuring programs linked against older library versions continue to function with newer versions without recompilation.
2:34 Motivation for BC: A core example illustrates how adding a default argument to a function, while source compatible, breaks binary compatibility, necessitating patch releases and the introduction of overloads. This leads to further issues, such as breaking code that takes function addresses for use in algorithms.
6:33 API vs. ABI: The presentation distinguishes between the Application Programming Interface (API), what the compiler sees, and the Application Binary Interface (ABI), what the linker sees. Changes to the API (e.g., adding default arguments) may not affect the ABI, but changes to the ABI (e.g., function signature changes affecting name mangling) inherently break BC.
7:49 KDE & Qt Experience: Both KDE and Qt promise BC within major releases, having developed extensive experience and established patterns for managing compatibility over two decades, though not without occasional mistakes.
17:17 Name Mangling: The process of creating unique symbol names by encoding function signatures, arguments, and scope is explained. Changes to name mangling inputs (platform-dependent) break the ABI, while changes to non-inputs may be BC.
21:17 Exported Symbols: The distinction between symbols callable from outside a dynamic library (exported) and internal symbols is critical. On Unix, symbols are exported by default unless explicitly hidden; on Windows, they must be explicitly exported. Macros like Q_DECL_EXPORT/Q_DECL_IMPORT (or similar for other libraries) manage this.
25:32 Golden Rules for Exporting Classes: Polymorphic classes should generally be exported wholesale to ensure their vtables are exported. Destructors should be defined out-of-line. Non-polymorphic classes should avoid wholesale export; individual functions and variables should be exported selectively. Exported symbols form the public API and are the primary source of BC issues.
28:56 "Remove Since" Technique: A key strategy involves creating a new translation unit (removed API.cpp) that defines a "removed since" macro. This allows a function to be removed from the API (for new callers) while remaining ABI compatible by implementing it as a cast to the new function or a stub. This mechanism automates the cleanup of deprecated API elements.
48:23 Configurable Binary Compatibility Window: Qt employs a mechanism where a user-configurable cutoff version defines the lower bound of the BC window, allowing flexibility in evolving the API.
49:33 Key Impediments to ABI Evolution: Wholesale exported classes and virtual functions are identified as the primary obstacles to ABI evolution, particularly due to the challenges in replacing or adding virtual functions without breaking existing binaries.