Skip to content

Case 124 โ€” Header Constant Value Changed

Field Value
Verdict ๐ŸŸ  API_BREAK
Category API Break
Platforms Linux, macOS, Windows
Flags API break
Detected ChangeKinds constant_changed
Source files examples/case124_header_constant_value_changed/

Verdict: ๐ŸŸ  API_BREAK abicheck verdict: API_BREAK (with headers) / NO_CHANGE (object/ELF-only)

What changes

Version Declaration
v1 constexpr int kMaxChannels = 8;
v2 constexpr int kMaxChannels = 16;

A public compile-time constant changes value. A namespace-scope constexpr (or const) has internal linkage โ€” it emits no exported symbol.

What breaks

The value is baked into every consumer at compile time (e.g. float buf[kMaxChannels]). A consumer built against v1 carries 8; one built against v2 carries 16. Mixing them โ€” an old consumer against the new library, or two translation units compiled against different header versions โ€” produces disagreeing buffer sizes and out-of-bounds access. A silent ABI-contract break.

Why this case exists โ€” invisible to object analysis

Because the constant has no symbol, the v1 and v2 .so files are byte-identical at the ABI level โ€” object/DWARF comparison sees NO_CHANGE. abicheck detects it only in header mode: castxml exposes the init="16" initializer, and the dumper extracts constants declared in the provided public headers (not ones pulled in transitively from system/private headers), so the finding is public-contract by construction (ChangeKind constant_changed).

Note: this covers const/constexpr constants. Plain #define macro constants are still invisible โ€” castxml emits no macros (a separate documented gap; see Limitations).

Reproduce manually

g++ -shared -fPIC -g v1.cpp -o libaudio_v1.so
g++ -shared -fPIC -g v2.cpp -o libaudio_v2.so
abicheck compare libaudio_v1.so libaudio_v2.so \
    --old-header v1.h --new-header v2.h   # โ†’ API_BREAK (constant_changed 8 โ†’ 16)
abicheck compare libaudio_v1.so libaudio_v2.so # โ†’ NO_CHANGE (object-only)

How to fix

Treat public compile-time constants as part of the ABI contract: avoid changing their values across compatible releases, or expose the value through an exported accessor function (int max_channels();) so consumers read it at runtime instead of baking it in.


Source files

  • CMakeLists.txt
  • app.cpp
  • v1.cpp
  • v1.h
  • v2.cpp
  • v2.h

See also: Examples overview ยท All API_BREAK cases ยท Category: API Break.