The Ecosystem Gap: What the C++ Standard Cannot Specify
Great Founder Theory in C++
Document: P9999R0
Date: 2025-12-18
Audience: EWG, LEWG, SG15
Reply-to: Vinnie Falco <vinnie.falco@gmail.com>
The Ecosystem Gap: What the C++ Standard Cannot Specify
Abstract
The C++ Standard specifies the language and its standard library. However, practical C++ development depends on numerous aspects outside the standard’s scope: Application Binary Interface (ABI), binary formats, debug information, build systems, package management, and tooling. This paper examines these “ecosystem” concerns, contrasts C++ with Python’s approach to backwards compatibility, and analyzes the committee’s decision to prioritize ABI stability through the lens of institutional dynamics. We argue that the standard’s structural inability to address ecosystem concerns, combined with the committee’s de facto ABI stability policy, creates compounding technical debt that undermines C++’s core value proposition of zero-cost abstractions.
1. Introduction
The ISO C++ standard (ISO/IEC 14882) defines the syntax, semantics, and standard library of C++. What it does not define is equally important: how compiled C++ modules interoperate at the binary level, how developers obtain and integrate third-party libraries, or how tools analyze and transform C++ code. These “ecosystem” concerns are left to implementations, platform vendors, and community convention.
This division of responsibility is not accidental. The standard deliberately avoids implementation details to permit optimization and platform adaptation. Yet this same flexibility creates fragmentation that impedes the ecosystem’s development. A library compiled with GCC cannot link against code compiled with MSVC. A package manager that works on Linux may fail on Windows. Debug symbols from Clang cannot be read by Visual Studio’s debugger.
The Python ecosystem, by contrast, demonstrates what becomes possible when a language community makes different tradeoffs. Python’s willingness to break backwards compatibility—most dramatically in the Python 2 to Python 3 transition—enabled the creation of a unified package ecosystem (PyPI) with over 700,000 packages. The cost was a decade of migration pain. The benefit was a coherent ecosystem that C++ developers can only envy.
This paper examines what the C++ standard cannot specify, why it cannot specify these things, and what consequences flow from these limitations.
2. The C++ Ecosystem: What the Standard Does Not Define
2.1 Application Binary Interface (ABI)
The ABI encompasses all conventions necessary for separately compiled modules to interoperate: name mangling, calling conventions, object layout, vtable structure, RTTI format, and exception handling mechanisms. None of these are standardized by WG21.
Name mangling illustrates the fragmentation. GCC and Clang follow the Itanium C++ ABI, producing symbols like _Z3fooi for void foo(int). MSVC uses a different scheme, producing ?foo@@YAXH@Z. These are incompatible. Code compiled with GCC cannot be linked against code compiled with MSVC, even when both compilers target the same platform and implement the same C++ standard version.
Object layout presents similar challenges. The standard permits implementations to choose padding, alignment, and virtual base placement. The result is that sizeof(T) may differ across compilers. A structure with particular member ordering may have different layouts. Virtual inheritance, multiple inheritance, and empty base optimization all have implementation-defined behaviors that affect ABI.
Exception handling mechanisms diverge sharply. The Itanium ABI uses DWARF-based unwinding; MSVC uses Structured Exception Handling (SEH) on Windows. These mechanisms are architecturally incompatible—not merely different conventions, but fundamentally different approaches to stack unwinding and cleanup.
2.2 Binary Formats and Debug Information
Object file formats are platform-specific: ELF on Unix-like systems, PE/COFF on Windows, Mach-O on macOS. Debug information formats similarly diverge: DWARF on Unix-like systems, PDB on Windows. The standard has nothing to say about any of these.
This is defensible—object formats are platform concerns, not language concerns. Yet the practical effect is that C++ tooling must be reimplemented for each platform. A debugger written for Linux cannot read Windows debug symbols. A profiler targeting macOS cannot analyze ELF binaries. The fragmentation multiplies the effort required to build cross-platform tools.
2.3 Standard Library Implementations
Three major implementations of the C++ standard library exist: libstdc++ (GCC), libc++ (LLVM), and the MSVC STL. These implementations are ABI-incompatible with each other. They are sometimes ABI-incompatible with themselves across versions.
The most notorious example is std::string. Prior to C++11, libstdc++ implemented std::string with copy-on-write (COW) semantics. The class layout was a single pointer to a heap-allocated block containing the data, size, capacity, and reference count. C++11 prohibited COW implementations due to thread-safety concerns. The new implementation uses Small String Optimization (SSO), with an entirely different layout: three words containing pointer, size, and either capacity or inline character data.
Code compiled against the COW std::string cannot interoperate with code compiled against the SSO implementation. A library built in 2010 cannot be linked with code built in 2015, even if both use the same compiler from the same vendor. The ABI break forced a choice: maintain backwards compatibility with pre-C++11 binaries, or adopt the conforming implementation. Some distributions still default to the pre-C++11 ABI years after the standard changed.
2.4 Build Systems and Package Management
The C++ standard specifies no build system. CMake has emerged as the de facto standard through market dominance rather than standardization. Even so, CMake usage is far from universal, and CMake itself is merely a meta-build system that generates native build files.
Package management is worse. No standard package manager exists. vcpkg, Conan, and Spack each have significant adoption and incompatible approaches. Many C++ developers manage dependencies manually, copying source files into their projects. The phrase “header-only library” is a marketing advantage because it sidesteps the linking problems that plague the ecosystem.
The cultural consequences are significant. C++ developers are famously reluctant to introduce dependencies. Libraries advertise “no external dependencies” as a virtue. This reluctance is rational given the ecosystem’s fragmentation—each dependency introduces potential ABI incompatibility, build system complexity, and platform-specific failures. The result is that C++ developers frequently reimplement functionality that would be a one-line import in other ecosystems.
3. The Python Contrast: A Different Philosophy
3.1 Unified Ecosystem by Design
Python’s ecosystem emerged from a fundamentally different philosophy. The Python Package Index (PyPI) launched in 2003, providing a centralized repository for Python packages. pip, the standard package installer, was introduced in 2008 and became the recommended tool by 2014. Today, PyPI hosts over 700,000 packages, and installing a dependency is typically a single command: pip install package-name.
This unity did not happen by accident. Python’s governance, initially through Guido van Rossum as “Benevolent Dictator for Life” (BDFL) and later through a Steering Council, enabled coherent decision-making about ecosystem infrastructure. The Python Software Foundation provided organizational continuity. The Python Enhancement Proposal (PEP) process created a clear mechanism for proposing and adopting standards, including ecosystem standards like PEP 517 (build system interface) and PEP 518 (build dependencies).
3.2 The Python 2 to Python 3 Transition
Python’s most dramatic backwards compatibility break occurred with Python 3.0, released in December 2008. The changes were substantial: print became a function, integer division behavior changed, strings became Unicode by default, and numerous standard library modules were reorganized or removed.
The transition was, by any measure, painful. It took over a decade for Python 3 adoption to surpass Python 2. Major libraries resisted migration for years. The ecosystem fragmented between Python 2 and Python 3 versions of packages. Some developers characterized the transition as “horrific” and estimated its cost in the hundreds of billions of dollars of industry effort.
Yet the transition eventually completed. Python 2.7 reached end-of-life in January 2020. Today, virtually all active Python development targets Python 3. The painful migration is over. The language emerged with cleaner semantics, better Unicode handling, and a unified ecosystem that could move forward without perpetually supporting legacy decisions.
3.3 Why Python Could Break Compatibility
Python’s ability to execute a breaking change stemmed from several factors that C++ lacks.
First, Python has no ABI in the C++ sense. Python code is distributed as source or bytecode, not as compiled binaries. The interpreter handles compatibility—if the interpreter can run the code, it runs. There are no symbol mangling incompatibilities, no layout differences, no calling convention mismatches. The entire class of problems that plague C++ binary distribution simply does not exist.
Second, Python had centralized governance. Guido van Rossum made the decision to break compatibility, and the decision stood. The BDFL model concentrated authority in a single individual with the legitimacy and technical vision to make difficult tradeoffs. When Guido said Python 2 would eventually end, the ecosystem adapted—not immediately, not willingly, but eventually.
Third, Python’s value proposition differs from C++’s. Python optimizes for developer productivity, not execution performance. The cost of an interpreter restart or a recompiled dependency is measured in seconds, not hours. Breaking changes impose costs, but those costs are bounded by the speed of a pip install.
4. The C++ ABI Stability Decision
4.1 The Prague Vote
In February 2020, at the Prague meeting, WG21 took a series of polls on whether to break ABI for C++23. The committee voted against breaking ABI. As one observer noted, “there was no applause.”
The decision formalized what had been implicit practice. Formally, the C++ standard says nothing about ABI—it is entirely outside the standard’s scope. Yet implementers have historically held a de facto veto on any proposal that would break ABI stability. The Prague vote made this veto explicit policy.
Paper P2028R0 (”What is ABI, and What Should WG21 Do About It?”) outlined the stakes: “If we promise ABI stability, I believe that industry involvement in (and dependence upon) the standard library will diminish. Concretely, I believe that Google’s interest in the standard library will be limited to primitives that are demonstrably efficient, are expensive to copy, and that commonly pass through interface boundaries.”
4.2 The Cost of ABI Stability
ABI stability imposes constraints that accumulate over time. Every standard library type’s layout is frozen once shipped. Every function signature is permanent. Every decision, optimal or not, becomes permanent.
The consequences are concrete. std::regex is notoriously slow—slower, in some benchmarks, than spawning a PHP process to execute a regular expression. It cannot be fixed without breaking ABI. std::unique_ptr has overhead compared to raw pointers due to calling convention constraints. std::unordered_map’s API and ABI force suboptimal implementation strategies compared to alternatives like Swiss Tables.
int128_t has never been standardized because modifying intmax_t would break ABI. scoped_lock was added rather than fixing lock_guard to avoid ABI breakage. Features that would require ABI changes are rejected or redesigned to work within ABI constraints, regardless of technical merit.
P1863R0 estimated the cost of an ecosystem-wide ABI break in “engineer-millennia”—coordinating rebuilds for every provider of a plugin, shared library, or DLL worldwide. This estimate explains the reluctance to break. It also suggests that the longer ABI remains stable, the more expensive any eventual break becomes. Each passing year compounds the problem.
4.3 The Performance-Stability Trilemma
As articulated during the Prague discussions, there exists a fundamental trilemma: performance, ABI stability, and ability to change. You can have at most two.
C++ historically has prioritized performance and ability to change—”zero-cost abstractions” and “don’t pay for what you don’t use” are core principles. ABI stability runs directly counter to both. If a more efficient layout is discovered for std::string, ABI stability prevents adopting it. If a better algorithm exists for std::sort, ABI stability may prevent inlining changes that affect codegen.
The committee voted to prioritize stability, but without explicitly acknowledging the tradeoff against the language’s historical principles. This incoherence—”we say ‘performance’ but vote ‘ABI’”—creates confusion about the language’s direction and erodes trust in the standard library’s fitness for performance-critical work.
5. Institutional Analysis: Why C++ Cannot Do What Python Did
5.1 Governance Structure
WG21 is an ISO working group operating under consensus procedures. Proposals must navigate multiple study groups (SG1-SG23+), working groups (EWG, LEWG, LWG, CWG), and ultimately plenary votes. This structure ensures broad input but makes decisive action difficult.
Contrast this with Python’s BDFL model. When Guido van Rossum decided Python 3 would break compatibility with Python 2, that decision was final. There was no vote. There was no consensus-building among competing interests. The decision was made by someone with the authority, legitimacy, and technical vision to make it.
C++ has no equivalent figure. Bjarne Stroustrup created C++ and remains active in its evolution, but he participates through the same process as everyone else—writing papers, seeking consensus, navigating committee procedures. No individual has the authority to mandate an ABI break over vendor objections.
5.2 Vendor Dynamics
C++ implementation is dominated by a small number of vendors: GCC (Red Hat, GNU), Clang (Apple, Google, LLVM Foundation), and MSVC (Microsoft). These vendors have customers with vast codebases compiled against current ABIs. Breaking ABI imposes costs on those customers—costs measured in engineering time, testing effort, and deployment risk.
Vendors can effectively veto ABI-breaking changes by refusing to implement them. If Microsoft decides MSVC will maintain ABI stability, any standard requirement that breaks MSVC ABI will be ignored or worked around. The standard cannot compel vendor behavior; it can only specify what conforming implementations must do.
Python’s vendor dynamics differ dramatically. CPython is the reference implementation, maintained by the Python Software Foundation and a community of contributors. Alternative implementations (PyPy, Jython, IronPython) exist but follow CPython’s lead. There is no Microsoft equivalent insisting on backwards compatibility against the community’s direction.
5.3 The Succession Problem
Python managed a governance transition. When Guido stepped down as BDFL in 2018, the community had mechanisms—PEPs, the PSF, established contributor relationships—to transition to a Steering Council model. The transition was orderly. Python development continued without interruption.
C++ faces a different succession challenge. Bjarne Stroustrup, now approximately 75 years old, remains active but cannot participate forever. Herb Sutter has served as convener since 2009. When these figures eventually step back, who succeeds them? What happens to the technical vision they have maintained?
More broadly, C++ has lost knowledge across transitions. The original C++0x Concepts implementation (ConceptGCC) was abandoned when Concepts were pulled from C++11. The redesign that led to C++20 Concepts started largely from scratch. Design rationale from early committee discussions is often lost; papers expire, meeting minutes are incomplete, and the tacit knowledge of why certain decisions were made disappears with the people who made them.
5.4 Process Ossification
WG21’s process was designed for standards development, not ecosystem coordination. It excels at achieving consensus on language features among compiler vendors and library implementers. It has no mechanism for mandating build system standards, package management conventions, or ABI specifications.
The committee can recommend. It cannot compel. When SG15 (Tooling) discusses build system improvements, no path exists to make those improvements binding. When SG16 (Unicode) develops text handling facilities, it cannot mandate that all compilers support UTF-8 source files identically. The standard’s scope is structurally limited to what implementations voluntarily adopt.
This is not a bug but a feature of ISO standardization. ISO standards describe; they do not govern. The same flexibility that allows C++ to target diverse platforms—from microcontrollers to supercomputers—prevents the kind of ecosystem unification that Python achieved through centralized authority.
6. Consequences and Implications
6.1 The Standard Library’s Diminishing Relevance
If ABI stability prevents the standard library from evolving, users who need performance will abandon it. Google has already signaled this direction: Abseil provides alternatives to standard library facilities with different performance characteristics. Facebook’s Folly, Bloomberg’s BDE, and numerous other corporate libraries exist partly because the standard library cannot meet their requirements.
The irony is acute. The standard library exists to provide a common vocabulary for C++ programs. If performance-conscious users abandon it for alternatives, the vocabulary fragments. Different organizations use different string types, different containers, different threading primitives. The standard library becomes a least-common-denominator solution for projects that can tolerate its limitations.
6.2 Competitive Pressure
Rust demonstrates an alternative model: aggressive evolution with explicit edition boundaries, a unified package ecosystem (crates.io with over 150,000 crates), and a governance structure that can make decisions. Memory safety concerns from organizations like the NSA and CISA add urgency. Bjarne Stroustrup’s March 2025 “call to defend C++” acknowledged what many perceive as an existential threat.
Python shows what a unified ecosystem looks like. Rust shows that systems languages can have unified ecosystems too. C++’s fragmentation is not an inevitable consequence of targeting native code; it is a consequence of choices—choices that could theoretically be made differently.
6.3 The Path Not Taken
What would a C++ ABI break look like? P1863R0 outlined options: never break (accept permanent performance limitations), break unpredictably (as with C++11’s std::string), break on a predictable schedule (every N years), or break constantly (no stability guarantees).
Python’s experience suggests that a planned, signaled breaking change—announced years in advance, with tooling support for migration, and a hard cutoff date—is survivable. The transition is painful but finite. The alternative—permanent accumulation of technical debt—is infinite pain, distributed across all future development.
The committee chose against breaking. Whether this was wise depends on time horizon. In the short term, stability protects existing investments. In the long term, stability may strangle the language’s ability to compete with alternatives that made different choices.
7. Conclusion
The C++ standard cannot specify what the C++ ecosystem needs. ABI, build systems, package management, and tooling lie outside its scope. This is a structural limitation, not a failure of will.
Within this structure, the committee has chosen ABI stability over the ability to correct past mistakes. This choice has consequences: a standard library that cannot be optimized, features constrained by layout decisions made decades ago, and a growing gap between what the standard library offers and what users need.
Python’s example demonstrates that breaking changes are survivable. The Python 2 to Python 3 transition was painful, expensive, and controversial. It also completed. The language emerged coherent, the ecosystem unified, and development continues.
C++ has chosen a different path. Whether that path leads to sustainable evolution or gradual obsolescence remains to be seen. What is certain is that the choice has been made, the consequences are accumulating, and the ecosystem gap continues to grow.
8. Acknowledgments
The author thanks Peter Dimov for his pragmatic assessments of standard library facilities, Titus Winters for his detailed analysis of ABI concerns in P1863R0 and P2028R0, and the numerous committee members whose papers and discussions informed this analysis.
9. References
[P1654R0] Ben Craig, “ABI breakage - summary of initial comments”, 2019
[P1863R0] Titus Winters, “ABI - Now or Never”, 2019
[P2028R0] Titus Winters, “What is ABI, and What Should WG21 Do About It?”, 2020
[PEP 387] Python Backwards Compatibility Policy
[PEP 13] Python Language Governance
[Itanium C++ ABI] https://itanium-cxx-abi.github.io/cxx-abi/
Burja, Samo. “Great Founder Theory”, 2020


Even granting the premise that the C++ standard cannot specify its environment, it isn’t obvious that this pushes us toward abandoning ABI stability. If anything, the absence of a specified environment raises the bar for changes that require global coordination. When a language has no reference runtime, no distribution channel, and no authority to enforce synchronized upgrades, the only leverage it has is whatever contracts the ecosystem already honors. The question isn’t whether the standard can define the environment. It can’t. The question is whether breaking ABI assumes a kind of control the language has never actually possessed.
Hi Vinnie,
I just went through P9999R0 and you raise some critical points--ones I think are overdue for our consideration. Here are some further points:
- The Cargo Advantage: It’s becoming impossible to ignore that Rust’s Cargo is a fundamental competitive advantage over C++. It’s not just about "convenience"; it’s about the velocity of the entire ecosystem. Cargo is a delight to use; and, in my opinion, one of Rust's strongest advantages. C++ _must_ create a similar tool to remain competitive.
- The Meritocracy Gap: We currently have no mechanism to embrace a true meritocracy for data structures. Why can't we have a "blessed" competition for things like Swiss Tables or better concurrency primitives? We need a way for the committee to provide "security and performance reviews" for external packages so they can be adopted with confidence without waiting a decade for an ISO seal. Empirically-tested data structures and packages should win the day, and receive the full recommendation from the C++ community. The strongest solutions should rise to the top. Move fast and promote strong packages. Move fast and improve things.
- Missing Modern Pillars: We are still struggling with basics while the industry has moved to IPC, rich thread pools, cross-platform inotify, C++ pandas/jupyter notebook kernels, distributed programming, and distributed concurrency. C++ should be leading here, but the lack of standardized "ecosystem tools" makes these tasks feel like reinventing the wheel every time.
- Delight as a Metric: Even the most advanced systems engineers appreciate a polished, easy-to-use interface. There’s a misconception that making things easy to use is "appealing to the lowest common denominator." It’s actually the opposite. It’s about making high-performance tools delightful to use so that we can focus on solving the actual problem, not the boilerplate.
- No Inroads: I want to teach my kids C++. I want it to be their first language. I don't want to teach them python as their first language. Where do I even start? Godbolt some examples? How do engineers start their careers in C++? The fragmentation and lack of "promoted" packages is also present in training materials. C++ should have an answer for this. Include people. Make C++ more inclusive by getting organized and promoting the strongest option. The C++ Guidelines are a great start, but we need more. We can do much better.
The "infinite pain" of technical debt you mentioned is real. Thanks for putting this on paper so clearly.
Best,
Steve