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/constexprconstants. Plain#definemacro constants are still invisible โ castxml emits no macros (a separate documented gap; seeLimitations).
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.txtapp.cppv1.cppv1.hv2.cppv2.h
See also: Examples overview ยท All API_BREAK cases ยท Category: API Break.