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:
#[repr(C)] unionis one storage and several typed views. Size is the largest member, alignment is the strictest, verified field-for-field against clang. Members must beCopy: a union has no tag, so no destructor could be run correctly. An either/or value is still a tagged enum.#[repr(C, packed)]and#[repr(C, packed = N)]are C's packed records.#[bits(N)]is a C bitfield, with C's packing rule, signed reads, and read-modify-write stores. Neither a bitfield nor an under-aligned packed field has an address.- A payload-free enum takes explicit discriminants and
#[repr(u8)]…#[repr(i64)], which is what it lowers to and what it crosses the C ABI as.
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:
impl Point: Eq {}generates the memberwise implementation. Same idiom as aSendmarker. A struct with derivedHash+Eqis a validHashMapkey.#[requires(n > 0)]and#[ensures(result >= n)]are machine-checked contracts. Pureboolexpressions, emitted asassert, checked in every profile.- An interface method may carry a body. Implementors may omit it. The body is copied into each impl, so
Thismeans that type and there is nodyn.
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
cpc-bindgen --gobjectis the Linux analog of--framework. The generated stack replaces the hand-written GTK/Adwaita packages:glib,gobject,gio,cairo,pango,gdk,gtk4,adwaita.--cpackagedrives a pkg-config C package. Bitfields and packed records come out as language forms, not guessed accessors.sqliteis an idiomaticConnection/Statementover a raw bindgen base.quartzcoremakesNSView.layer()a realCALayer.cpc test --asan/--release/--timings.
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