A modern cross-platform C++17 library providing unified access to native system APIs. Platform
details are hidden behind a per-module seam; an optional C API layer (src/capi/) exposes
everything for FFI from Dart, Rust, C# and others.
include/nativeapi.h # single public include
src/
├── foundation/ # events, dispatch, handle table, ID allocation, geometry
├── capi/ # C ABI bindings — 27 of 28 headers are GENERATED
├── platform/ # windows, macos, linux, android, ios, ohos — one is built
└── *.h, *.cpp # cross-platform interface definitions (25 public headers)
examples/ # 23 examples; these double as integration tests
The design rules for this library live in the libnativeapi workspace repo, under
specs/ — one directory up when this repo is checked out as the workspace's core/
submodule. Read them before adding or reshaping public API:
| Spec | Covers |
|---|---|
../specs/architecture.md |
Layering, the six-platform matrix, naming, build |
../specs/object-model.md |
Identity objects vs value objects |
../specs/api-style.md |
Method vocabulary, parameter/return types, failure reporting, doc comments |
../specs/platform-seam.md |
PIMPL, narrow seams, NativeObjectProvider |
../specs/event-system.md |
Event / EventEmitter, threading, lazy listening |
../specs/managers.md |
Singletons, registries, the handle table |
../specs/c-abi.md |
What codegen produces and how types cross the boundary |
../specs/handle-ownership.md |
C ABI handle ownership and invalidation |
Each spec also records the open questions and known legacy gaps of its own area inline; there is no separate issue list.
- No platform types in public headers — no
HWND,NSWindow*,GtkWidget*, no<windows.h>, no#ifdefplatform branches insrc/*.h. - A new cross-platform module is six files — every directory under
src/platform/needs an implementation, or that platform fails to link. - PIMPL discipline — forward-declare
class Impl, holdstd::unique_ptr<Impl> pimpl_, define the destructor in the.cpp, delegate between constructors. - Emit events through
EventEmitter<T>;EmitAsynclands on the main thread, and any class using it must callShutdownEmitter()first thing in its destructor. - Expose native handles only via
NativeObjectProvider, and never transfer ownership. - Never hand-edit
src/capi/— change the C++ header and run./codegenfrom the workspace root. Files carrying// AUTO-GENERATED. DO NOT EDIT.are overwritten. - New handle types need an
IdTypeTag<T>entry in src/foundation/id_allocator.h — append only, never renumber.
The full rules and the review checklist are in ../specs/api-style.md. Everything public in
src/*.h is exported verbatim to C, Dart, Rust and C#, so the short version is:
- Properties are
SetX(v)+GetX() const, booleansSetX(bool is_x)+IsX() const, declared as an adjacent pair. Getters are alwaysconst— codegen only turnsconstno-argGet/Is/Hasmethods into binding properties. - Actions are bare imperative verbs with a fixed opposite (
Show/Hide,Open/Close,Maximize/Unmaximize,Enable/Disable) and anIsXxx() conststate query. - No new overloads — they surface as
native_x_verb_with_<params>. Different meaning, different name (RemoveItemById,RemoveItemAt). - Types: strings in as
const std::string&, out by value; geometry and enums by value; identity objects only asstd::shared_ptr<T>(nullptr= none), neverconst T&/T*;std::optionalwraps strings only; floating point isdouble; IDs use theXxxIdalias. - Enums:
enum class, PascalCase values, nokprefix, sequential from 0, first value is the neutral default, append only. - Failure: never throw across the public API.
voidwhen it cannot fail,boolwhen a platform may not support it (document whatfalsemeans),nullptrfor lookups and factories. Do not invent another error channel. - Platform differences live in the doc comment, not the signature: every API exists on
all six platforms, and one that behaves differently carries the six-line
@note Platform availability:block (✅ /⚠️ / ❌). - "Intercept before X" is a cancellable event on the object, not another
SetWillXxxHook; internal entry points stay out of thepublic:section. - After editing a header, run
./codegenfrom the workspace root and make sure noskippedwarning names the new API.
Where existing headers disagree, the spec says which side is the rule — do not copy the nearest neighbour.
- C++ — classes and methods
PascalCase, memberssnake_case_, filessnake_case.h, platform files<module>_<platform>.<ext>. - C — handles
native_*_t(uint64_t), functionsnative_<module>_<verb>, enumsNATIVE_*, files*_c.h. All generated; do not write them by hand.
CMake, C++17, propagated via target_compile_features(nativeapi PUBLIC cxx_std_17).
src/CMakeLists.txt selects exactly one platform directory per build. Verify changes
against the relevant program in examples/.
When this repo is the core/ submodule of the libnativeapi workspace, a change to any
public header ripples into the generated bindings — run ./codegen sync from the
workspace root rather than hand-editing generated files.
- Do not add Co-Authored-By trailers to commits.