C+
August 14, 2026 ·The C+ team

Announcing C+ 0.0.27

v0.0.27 is the release that closes the last gaps between a C+ type and a C header, and between a str view and the storage it points at. Bindgen can describe the records real headers contain. The checker can see a view through every form it used to leak. facet is no longer a three-element spike: it is an app, with screens, theme, services, and a second backend.

None of this adds a runtime. Layout, contracts, and derived methods are ordinary code the optimizer already sees. Safety still costs no performance.

The C ABI, without a lie

A binding generator that cannot say what a header says will invent a layout. v0.0.27 gives it the words:

cpc-bindgen emits all three. What it used to emit for a bitfield was a single u32 slot and a read-only accessor that assumed every field lived in that first word. That was wrong for a second run, for storage wider than 32 bits, and for every signed field. A record under #pragma pack(N) is now refused: clang's JSON AST records that a maximum field alignment applies without saying what it is, and a guessed layout is the failure a binding generator exists to prevent.

Views the checker can see

Nine ways a str or slice of storage that dies at return could escape are now rejected. The checker follows the shape, not a method-name allowlist: any accessor that returns a view, a take parameter, a free function, a temporary, a field store, a static, a ref out-parameter, a slice of a Vec. A mutating method on a borrowed place was already refused; a plain field write, and passing the place as a ref argument, now are too.

thread::scope is the other half of that model. Scope::lend hands a parent local to a worker as a ref. Scope::drop joins every worker, on every path out. No Arc, no copy back. The loan is a borrow the checker can see (#[keeps(this)]). The lent value dying first, a write while a worker holds it, or lending the same place twice are compile errors, not races.

Derive, contracts, defaults

Three small language additions, all ordinary AST before type-check:

Also in this release: checked narrowing as? (Option[T], no silent truncate), type UserId = distinct i64 (same ABI, not interchangeable), const expressions (const MASK: u64 = (1u64 << 40) - 1u64), guard var / if var / while var, and f::[T] as a function-pointer value.

facet is an app

The @facet { label / button / stack } spike is gone. You write @ui trees against facet/elements. State lives in a Component. Screen names the window. App is the process: named routes, a menu bar, app.run(initial). Updates are keyed and direct. There is no re-render.

Theme is two tiers: platform semantic colours, and app-retintable roles (primary, surface, ink(a)). Color::adaptive(light:, dark:) is one value, resolved live. set_theme repaints the running app.

facet_appkit::install() fills the backend vtable; App::run mounts the window. facet_gtk fills the same vtable on GTK 4 (type-checked; not yet run on a Linux host). Layout is flex_layout, a pure-C+ Flexbox + Grid engine.

The agent surface defaults to the curated exposed view and re-walks the live tree on every request. agent_inapp is the same verbs with no socket. inspector walks the facet tree for a developer and is deliberately not the agent surface.

Bindings, data, tools

The standard library adds stdlib/slice (checked sub-views) and stdlib/flags (a u64 option-set), plus Box::into_raw / from_raw for the boundaries a Box cannot cross.

Install

brew install netdur/cplus/cplus
cpc --version

Linux and Windows builds are on the GitHub releases page. The manual is version 0.0.27; 0.0.26 and every earlier archive stay at /docs/{version}.


‹ Back to all posts