HN Debrief

Thoroughly Understanding C++ ABI (2024)

  • Programming
  • C++
  • Infrastructure
  • Developer Tools

The post is a deep explainer on C++ ABI, the binary-level rules that decide how functions are called, how classes and vtables are laid out, how exceptions work, and whether object files built by different compiler versions can actually interoperate. That matters because C++ source compatibility is not binary compatibility. Two pieces of code can both be valid C++ and still fail to link or behave correctly if they were built against different compiler, standard library, or platform ABI assumptions.

If your product ships plugins, shared libraries, SDKs, or cross-language bindings, treat ABI as an explicit design constraint instead of assuming "same language" means compatible binaries. When you need long-lived interoperability, prefer a narrow C or COM-style boundary and be cautious about exposing standard library types across it.

Discussion mood

Pragmatic and slightly weary. Most commenters accepted that C++ ABI is messy by design, not because vendors are incompetent, and they framed stable binary boundaries as a hard-won operational necessity after years of painful breakages.

Key insights

  1. 01

    COM solved the object ABI problem

    COM is a reminder that the hard part was never just function calls. It pinned down object layout, calling conventions, lifetime rules, activation, marshaling, security, and even asynchronous remoting, which is exactly what C++ leaves vendor-specific. That makes COM less of a Windows relic and more of a concrete answer to how you ship binary components across modules and languages without trusting a compiler's private object model.

    If you need binary plugins or multi-language components, study COM-style constraints before inventing your own interface layer. A precise object boundary beats exposing raw C++ classes and hoping every build stays aligned.

      Attribution:
    • Dwedit #1
    • quotemstr #1 #2
    • dblohm7 #1
  2. 02

    std::string shows how small spec changes explode outward

    The libstdc++ dual ABI episode was not an abstract standards spat. Tightening std::string for thread safety, contiguous storage, iterator guarantees, and constant-time access wiped out copy-on-write implementations and forced a long compatibility bridge. That is the clearest example of how a seemingly local library rule can turn into a decade-long migration problem once binaries and distros are involved.

    Do not expose standard library types casually in public binary interfaces. Even well-motivated library evolution can lock you into compatibility shims for years.

      Attribution:
    • aw1621107 #1
    • rramadass #1
    • bluGill #1
  3. 03

    ABI lives below the language standard

    The useful mental model is that C++ describes semantics, while ABI is a separate contract assembled from platform and toolchain layers. SysV covers the base calling convention on many Unix-like systems, Itanium fills in much of the C++ object and exception machinery, and Microsoft ships a different stack entirely. Once you think of ABI as platform law rather than language law, the interoperability failures stop looking mysterious.

    Document the exact compiler, standard library, target triple, and platform ABI your binaries depend on. "Built in C++" is not enough information for any serious distribution plan.

      Attribution:
    • jcranmer #1
    • mohamedkoubaa #1
    • jdw64 #1
  4. 04

    C became the default FFI because it is the lowest-friction target

    Several comments sharpened why C sits at the center of foreign-function interfaces. It is not that ISO C defines a universal ABI. It is that operating systems and toolchains often expose their binary contracts in C-like terms, and almost every language can map to that subset without wrestling with overloads, exceptions, templates, or vendor-specific object models. C won because it is simple enough to be everyone's common denominator.

    For SDKs meant to outlive a single compiler stack, a small C-facing layer is still the safest interoperability investment. You can keep richer C++ internals behind that boundary.

      Attribution:
    • pjmlp #1
    • mohamedkoubaa #1
    • bluGill #1
    • uecker #1
  5. 05

    Custom ABIs are viable inside controlled systems

    When you own both sides of the boundary, ABI stops being sacred and becomes another optimization knob. One commenter noted you can choose register conventions or other calling rules to fit your needs, and another pointed out this is used in obfuscation and binary protection work. That does not scale to public ecosystems, but it is a real option for tightly controlled deployments.

    If your binaries never leave your environment, you have more freedom than mainstream tooling suggests. Use it only when the operational gains outweigh the debugging and tooling costs.

      Attribution:
    • jeffbee #1
    • rramadass #1

Against the grain

  1. 01

    ABI breakage is survivable with the right distribution model

    The Linux-centric fear of ABI changes can be overstated. Microsoft broke ABI repeatedly for years, and the practical consequence was often that applications shipped with a matching runtime and stayed pinned to a compiler generation. That created friction, but it also shows ABI compatibility is partly a packaging and upgrade problem, not a purely technical taboo.

    Match your ABI policy to your distribution model. If you control deployment tightly, forced rebuilds may be acceptable. If you depend on broad third-party binary reuse, they are not.

      Attribution:
    • MFHava #1
    • Maxatar #1
    • plorkyeran #1
  2. 02

    C++ complexity can still be a rational trade

    The harshest anti-C++ line in the comments got pushback from someone arguing that the language's complexity is justified by the amount of power and real-world leverage it delivers. The important point is not that C++ is simple. It is that teams still choose it knowingly for performance-critical systems, large legacy bases, and hardware-near work where the alternatives do not slot in cleanly.

    Do not let ABI headaches alone drive language strategy. Evaluate whether your constraints actually require C++ and, if they do, invest in training and interface discipline instead of wishing the language were different.

      Attribution:
    • rramadass #1
    • mohamedkoubaa #1

In plain english

ABI
Application Binary Interface, the low-level binary contract that defines how compiled code calls functions, lays out data, handles exceptions, and links across modules.
COM
Component Object Model, Microsoft's binary standard for interoperable software components across languages and modules.
dual ABI
A compatibility strategy where a library supports two different binary interfaces at once so old and new code can coexist during migration.
FFI
Foreign Function Interface, a mechanism that lets code in one language call code written in another language.
IPC
Interprocess communication, mechanisms that let separate programs or processes exchange data and invoke services.
Itanium C++ ABI
A widely adopted specification that standardizes many C++ binary details on non-Microsoft platforms, despite the name no longer being limited to Itanium hardware.
libstdc++
The GNU implementation of the C++ standard library, commonly used with GCC.
MSVC
Microsoft Visual C++, Microsoft's C and C++ compiler toolchain and its associated binary conventions.
std::string
The standard C++ string type provided by the language's standard library.
vtable
Virtual table, a compiler-generated table of function pointers used to implement dynamic dispatch for C++ virtual methods.

Reference links

ABI references

C++ string and dual ABI history

Windows and COM

Related essays and books