C interop & ABI

extern C facades, opaque handles, out_ptr, #embed.

The facade pattern: extern "C" boundaries, opaque handle types, error codes at the edge, exceptions never crossing. Enum values and struct layout frozen by contract.

extern "C" {
  typedef struct stb_ctx stb_ctx;      // opaque
  stb_ctx* stb_open(const char* dev);
  int      stb_ioctl(stb_ctx*, unsigned op, void* arg);
}

No change; C++ side wraps handles in unique_ptr with custom deleters immediately at the boundary.

struct CtxClose { void operator()(stb_ctx* c) const { stb_close(c); } };
using Ctx = std::unique_ptr<stb_ctx, CtxClose>;

[[nodiscard]] on wrapper results, string_view absorbing const char* + length pairs, noexcept now part of function types (relevant to callback pointer types).

std::span replaces (pointer, length) parameter pairs at the boundary; bit_cast handles layout-compatible conversions without aliasing UB.

std::out_ptr/inout_ptr adapt smart pointers to T** C factory conventions in one line — the biggest pure-interop QoL fix in years.

Ctx ctx;
if (stb_open2(dev, std::out_ptr(ctx)) != 0) fail();

#embed reaches C/C++ parity for binary resources, and trivial relocation formalizes which types can safely cross memcpy-based C APIs.

C++29direction · not adopted

Direction: the perennial ABI-stability-vs-progress debate; nothing concrete adopted.

C++26 per the final working draft; C++29 rows are proposals and direction, not adopted features.