⚡ Aria

Modern C++20 MVVM framework · cross-platform · layered · coroutine-first

One shared core: Windows / macOS / Linux / iOS / Android / Web

C++20
License: MIT
Platform
Build
Tests

English | 简体中文 | HTML Version


🌟 Flagship example: AriaTools

Start with AriaTools to see Aria in a real application. It is Aria's single flagship cross-platform example, driving Qt, iOS, Android, and Web from one C++ ViewModel. This repository now stays focused on the framework, acceptance tests, and minimal documentation snippets.

🚀 Aria in 30 seconds

aria::Property<double> bill{100.0};
aria::Property<int> people{2};
aria::Computed<double> per_person{[&] {
    return bill.get() / people.get();
}};

// Property and Computed can both drive a View directly. The binding is
// released automatically when the View is destroyed.
engine.bind_text_projected(per_person, label_view,
    [](double value) { return std::format("¥{:.2f}", value); });

people = 4;  // label updates to ¥25.00

Property owns state, Computed tracks dependencies automatically, and BindingEngine projects read-only values into any UI host. Continue with the binding guide, the cookbook, or the full AriaTools application.

🎯 How it compares

Aria Qt Flutter React Native SwiftUI
Language C++20 C++ / QML Dart JS / TS Swift
Core size Header-only, ~0 100+ MB ~50 MB SDK ~200 MB node_modules Built-in
Reactive engine ✅ Auto dep-tracking (Computed, zero-config) ❌ Manual connect ✅ but locked to Flutter ✅ but locked to React ✅ but locked to Apple
C++20 coroutines Task<T> + co_await ⚠️ QCoroutine (limited)
ABI stable ✅ Type-erased layer, stable within major ⚠️ Partial
UI toolkit ✅ Any host (Qt / AppKit / UIKit / JNI / HTTP shipped; WASM remains triggered work) ❌ Qt only ❌ Flutter only ❌ React only ❌ SwiftUI only
Cross-platform ViewModel ✅ One C++ codebase, 6 platforms ❌ Rewrite QML per platform ⚠️ Dart cross-plat, non-native UI ⚠️ JS cross-plat, non-native UI ❌ Apple only
Web ✅ HTTP/SSE + WASM (planned) ✅ Web
Macro dependency Zero macros Heavy Q_OBJECT / SIGNAL / SLOT
License MIT LGPL / Commercial BSD MIT Apple proprietary

In short: aria extracts the reactive engine from the UI framework and makes it a pure C++20 header-only library. Pick any UI toolkit — one ViewModel runs on six platforms.

✨ Core features

🏗 Architecture (10 modules)

┌────────────────────────────────────────────────────────────────────────┐
│                         Application                                    │
└────────────────────────────────┬───────────────────────────────────────┘
              ┌──────────────────┼──────────────────┐
              ▼                  ▼                  ▼
   ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
   │ Qt6 adapter  │    │ JNI adapter  │    │ HTTP adapter │     (optional
   │ (Win/Mac/Lin)│    │  (Android)   │    │ REST/SSE Web │      modules;
   │ AppKit/UIKit │    │              │    │ WASM planned │      opt-in)
   └──────┬───────┘    └──────┬───────┘    └──────┬───────┘
          └───────────────────┴───────────────────┘
                              ▼
              ┌─────────────────────────────────┐
              │    aria-binding  (SHARED)       │
              │  BindingEngine + IViewAdapter   │
              └────────────────┬────────────────┘
                               │
        ┌──────────────────────┴───────────────────────┐
        ▼                                              ▼
┌───────────────────┐                       ┌────────────────────┐
│ aria-runtime      │                       │  aria-async        │
│ (SHARED .dylib)   │                       │   (header-only)    │
│ EventBus          │                       │ Task<T>            │
│ Container         │                       │ Scheduler          │
│ Dispatcher        │                       │ Executor           │
│ Logger            │                       │ schedule_on        │
└───────┬───────────┘                       └─────────┬──────────┘
        └──────────────────┬─────────────────────────—┘
                           ▼
              ┌─────────────────────────────┐
              │ aria-core  (header-only)    │
              │ Property / Computed / Cmd   │
              │ ObservableList / Validator  │
              │ Subscription                │
              └──────────────┬──────────────┘
                             ▼
              ┌─────────────────────────────┐
              │  aria-abi  (STATIC .a)      │
              │ Type-erased Signal/Slot     │
              │ ABI-stable, no templates    │
              └─────────────────────────────┘
Module Type Depends on Notes
aria-abi STATIC none Type-erased signal/slot. No templates. ABI-stable.
aria-core header-only abi All the templates: Property, Computed, Command, ObservableList, Validator. Source-compatible only (not ABI-stable).
aria-async header-only core C++20 Task<T>, executors. Source-compatible only.
aria-runtime SHARED core, abi EventBus / Container / Dispatcher / Logger — singletons live in one dylib. ABI-stable (non-template exports).
aria-binding SHARED core, runtime BindingEngine, IViewAdapter. ABI-stable (non-template exports).
Adapters SHARED/STATIC binding Qt6 / AppKit / UIKit / JNI / HTTP (each opt-in); WASM is planned.

📋 Requirements

Windows is supported on two toolchains: MSYS2 UCRT64 (GCC) and
MSVC / Visual Studio 2022.
Pick whichever fits your team's existing
stack — both build the full framework + tests + adapters from a single
tree, no source forks. See "Windows toolchains" below.

🚀 Quick start

git clone https://github.com/dqsjqian/Aria.git
cd Aria
cmake -B build/flavors/release -DCMAKE_BUILD_TYPE=Release
cmake --build build/flavors/release -j
ctest --test-dir build/flavors/release --output-on-failure

build/ is a container for build trees — never configure straight into
it. The unified build layout is documented at the top of
scripts/build.sh; the
per-flavor script scripts/build.sh [release|debug|asan|tsan] picks the
right directory for you.

First configure pulls doctest via the
bundled CPM.cmake. After that everything is offline.

One-liner build scripts

# macOS / Linux
scripts/build.sh             # release
scripts/build.sh tests       # release + ctest
scripts/build.sh asan        # debug + AddressSanitizer + UBSan
scripts/build.sh tsan        # debug + ThreadSanitizer
scripts/build.sh clean

# Windows — MSYS2 UCRT64 (GCC + Ninja)
scripts\build.ps1            # release
scripts\build.ps1 tests
scripts\build.ps1 asan
scripts\build.ps1 tsan       # debug + ThreadSanitizer (not available on MSVC, see below)

# Windows — MSVC / Visual Studio 2022
scripts\build-msvc.ps1       # release  (build/flavors/msvc/ tree)
scripts\build-msvc.ps1 tests
scripts\build-msvc.ps1 debug
scripts\build-msvc.ps1 asan  # /fsanitize=address (no UBSan on MSVC)

Windows toolchains

Aria ships with two parallel build scripts for Windows. They live
side-by-side in scripts/, write to separate build directories, and
neither one needs to know about the other.

Toolchain Script Build dir Notes
MSYS2 UCRT64 (GCC 14+ / Clang 18+) scripts\build.ps1 build/ Lightweight (~300 MB). Pre-installed on most CI images. Auto-detected from C:\msys64\ucrt64\bin and a few other common paths.
MSVC v143 (VS 2022) scripts\build-msvc.ps1 build/flavors/msvc/ Auto-detects the VS install via vswhere, scrubs MSYS2 env vars (INCLUDE / LIB / CPATH / ...) before running CMake, and uses the Visual Studio 17 2022 generator.

You can switch back and forth without clean — the two trees are
isolated. CI runs both nightly to make sure neither regresses.

MSVC one-time setup

# 1. Install Visual Studio 2022 Build Tools (or the full IDE) with
#    workload "Desktop development with C++" + "C++ CMake tools".
# 2. (Optional) install Qt 6 with the msvc2022_64 kit if you need the
#    Qt6 adapter.
# 3. From any PowerShell window:
scripts\build-msvc.ps1 tests

MSYS2 one-time setup

# 1. Install MSYS2 from https://www.msys2.org
# 2. Open the "MSYS2 UCRT64" shell:
pacman -Syu
pacman -S --needed mingw-w64-ucrt-x86_64-toolchain `
                   mingw-w64-ucrt-x86_64-cmake `
                   mingw-w64-ucrt-x86_64-ninja git
# 3. (Optional) Add C:\msys64\ucrt64\bin to your PATH.
# 4. From any shell:
scripts\build.ps1 tests

Rationale for shipping both: aria is coroutine-heavy C++20 code that
libstdc++, libc++, and the MSVC STL all handle cleanly. Pinning a
single Windows toolchain artificially excluded a large chunk of users
in the .NET / Visual Studio ecosystem — we now validate against MSVC
v143 on the same release gate as macOS, Ubuntu, and MSYS2.

Use it from your own project

Option A — find_package after install (recommended for production):

# In the aria tree:
cmake -S . -B build/flavors/release -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build/flavors/release -j && sudo cmake --install build/flavors/release
# In your project's CMakeLists.txt:
find_package(aria 1.0 REQUIRED)
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE aria::aria)
# or pick individual modules: aria::core / ::async / ::runtime / ::binding

Option B — vendored (no install):

add_subdirectory(third_party/aria EXCLUDE_FROM_ALL)
target_link_libraries(my_app PRIVATE aria::core aria::async)

Flagship example

AriaTools is the single flagship cross-platform example, driving Qt, iOS, Android, and Web from one C++ ViewModel, with all four shells gated in CI. It is also the reference for both Android integration shapes: the Compose side-channel and the typed JniAdapter. The Aria repository no longer carries application examples. Framework behavior is pinned by tests/acceptance/ and module tests, while these docs keep only focused, minimal snippets.

Build options

Option Default Description
ARIA_BUILD_TESTS ON Build unit tests + ctest registration.
ARIA_BUILD_BENCHMARK ON Build the micro-benchmark suite.
ARIA_BUILD_SHARED ON Runtime/binding as .dylib/.so/.dll instead of .a.
ARIA_BUILD_QT6 OFF Build the Qt6 adapter (requires Qt6Widgets).
ARIA_BUILD_APPKIT OFF (production-grade) Build the macOS AppKit adapter as a first-class STATIC CMake module using Objective-C++; ships aria::adapters::appkit and passes the shared adapter_conformance battery. Requires APPLE.
ARIA_BUILD_UIKIT OFF (production-grade) Build the iOS UIKit adapter as a first-class STATIC CMake module using Objective-C++; ships aria::adapters::uikit and passes the shared conformance battery. Requires APPLE.
ARIA_BUILD_JNI OFF Build Android JNI adapter as a first-class CMake module — built as STATIC, ships aria::adapters::jni, implementing the same IViewAdapter contract as Qt/AppKit/UIKit via reflective JNI dispatch (text / bool / int / double / visibility / click). Requires an Android NDK toolchain (NDK r26+ — the C++20-concepts core does not build under NDK r25's libc++).
ARIA_BUILD_WASM OFF (planned) Build WebAssembly adapter.
ARIA_ENABLE_ASAN OFF AddressSanitizer.
ARIA_ENABLE_UBSAN OFF UndefinedBehaviorSanitizer.
ARIA_ENABLE_TSAN OFF ThreadSanitizer.

👋 Hello, world

#include "aria/aria.hpp"
using namespace aria;

Property<int> count{0};

// No explicit dependency list — every Property::get() inside the lambda
// is auto-tracked on first evaluation.
Computed<std::string> label([&]{
    return "count = " + std::to_string(count.get());
});

Command<> increment([&]{ count = count.get() + 1; });

auto sub = label.bind([](const std::string& s) { std::cout << s << '\n'; });

increment();   // → "count = 1"
increment();   // → "count = 2"

⚡ Async (C++20 coroutines)

#include "aria/async/task.hpp"
#include "aria/async/executor.hpp"
using namespace aria::async;

Task<std::string> fetch_user(int id) {
    co_await schedule_on(network_pool);     // jump to worker thread
    auto raw = http::get("/users/" + std::to_string(id));
    co_await schedule_on(main_dispatcher);   // jump back to UI thread
    co_return parse(raw);
}

🌍 Cross-platform mapping

Platform UI host Adapter
Windows Qt6 / WinUI aria-qt6 ✅ ready (MSYS2 UCRT64 + MSVC 2022)
macOS AppKit / Qt6 aria-qt6 ✅ ready; AppKit ✅ ready
Linux Qt6 / GTK aria-qt6 ✅ ready
iOS UIKit / SwiftUI bridge UIKit ✅ ready; aria-uikit module planned
Android Compose / View aria-jni ✅ ready (NDK r26+)
Web (server-driven) HTML/JS in browser aria-http ✅ ready (REST + SSE)
Web (in-browser C++) DOM via WASM aria-wasm planned

The HTTP adapter ships a small server (HttpAdapter) that exposes any
ViewModel over a JSON REST + Server-Sent-Events protocol, plus a
vanilla-JS browser SDK (aria_client.js). The server is built on the
vendored single-header cpp-httplib (HTTP/1.1 + SSE) and
nlohmann::json (encode/decode) — both committed under
third_party/, so the adapter adds no new external build dependency;
aria itself owns the wire protocol, view registry, subscription dispatch
and SSE fan-out. It is the right shape for
desktop apps that want a web UI on the side, headless services, and
local debug dashboards. The WASM adapter — which compiles C++ business
logic into the browser sandbox — solves a different, more constrained
problem and remains on the roadmap. See
RFC 0001 for the design.

The current release ships the platform-agnostic core, runtime, async, and
binding layers — fully unit-tested. Qt6, AppKit, UIKit, JNI, and HTTP are
first-class opt-in adapters in the CMake tree (subject to their platform
requirements). WASM remains planned; the IViewAdapter interface is stable.

🧪 Test status

$ ctest --test-dir build/flavors/release --output-on-failure
Test project /…/aria/build/flavors/release
    Start 1: abi_tests           ✅ Passed
    Start 2: core_tests          ✅ Passed
    Start 3: fuzz_tests          ✅ Passed
    Start 4: async_tests         ✅ Passed
    Start 5: runtime_tests       ✅ Passed
    Start 6: binding_tests       ✅ Passed
    Start 7: qt6_tests           ✅ Passed   (when ARIA_BUILD_QT6=ON)
    Start 8: appkit_conformance  ✅ Passed   (Apple-only)
    Start 9: appkit_table_source ✅ Passed   (Apple-only)

100% tests passed, 0 tests failed (up to 9 suites, depending on options)

75+ individual test cases across the suites, including dedicated
regression tests for the lifecycle / re-entrancy / exception-safety
invariants pinned in docs/reference/lifecycle.md and docs/reference/error-model.md.

📊 Benchmark (Apple M-series, -O3 -DNDEBUG)

Operation ns/op
Property<int>::get() 10.4
Property<int>::set() no observers 28.5
Property<int>::set() 1 observer 29.3
Property<int>::set() 10 observers 45.9
Subscribe + auto-unsubscribe cycle 54.9
Computed chain x5 (set + recompute + get) 289.1
EventBus::publish (1 subscriber) 13.4
Container::resolve<Singleton> 7.6
10 sets wrapped in reactive::batch (notify once) 156.1
Batch update speedup vs individual 1.91×

📋 Framework contracts

Every non-trivial behaviour Aria promises is pinned in a numbered
contract document. Each contract item carries an ID (e.g. L-13,
E-22, LD-7, D-4, S-31) so a failing assertion or PR review
comment can point straight at the canonical description.

Document Prefix Scope
docs/reference/api-style.md S-N Naming, namespace, error and async-entry style
docs/reference/lifecycle.md L-N Threading, subscription, reactive flush, view-destroy, async cancel/dtor invariants
docs/reference/error-model.md E-N aria::Error / ErrorKind taxonomy and per-subsystem error contracts
docs/reference/list-diff-contract.md LD-N Insert / Remove / Replace / Move / Reset / ItemChanged semantics
docs/reference/diagnostics.md D-N aria::TraceEvent + aria::TraceSink protocol
docs/reference/performance.md PERF-N Complexity bounds and per-operation baselines for every public API

The P0 hard-bedrock pass (see CHANGELOG → Latest framework-grade
hardening
) closed every open contract above; the seven
framework-level fuzzers in modules/core/fuzz/ stress-verify the
lifecycle invariants (default 50k iterations / fuzzer; nightly runs
set ARIA_FUZZ_ITERS=1000000).

🗺 Capabilities

Capability Type Where
Reactive state Property<T> / Computed<T> / Effect aria/reactive/reactive.hpp
Commands Command<Args...> (reactive can_execute) aria/command.hpp
Collections ObservableList<T> + derived Filtered/Sorted/Mapped/Distinct/Grouped/Paged aria/observable_list.hpp, aria/derived/*
Selection Selection<T> / MultiSelection<T> (SE-1..SE-5) aria/selection.hpp
Validation Validator<T> / FormValidator / ValidationState + async rules aria/validator.hpp, aria/binding/form.hpp, aria/async/async_validator.hpp
Async Task<T> / AsyncCommand / with_timeout / when_any / when_all / CancellationToken aria/async/*
Data fetching AsyncResource<T> (SWR + dedupe) / Loadable<T> (5-state) aria/async/async_resource.hpp, aria/loadable.hpp
Navigation Navigator (push/pop/push_for_result<R>, route patterns) aria/binding/navigation.hpp
Binding BindingEngine / IViewAdapter / IView / Converter / bind_view_lifetime aria/binding/*
Diagnostics TraceEvent / TraceSink / GraphInspector (zero-overhead off) aria/diagnostics.hpp

Learn it: the documentation index links the guides,
the Cookbook (task-oriented recipes), and the
contract references. Build the symbol-level API reference with
cmake -B build/flavors/docs -DARIA_BUILD_DOCS=ON && cmake --build build/flavors/docs --target aria_docs.

🗺 Roadmap

Aria is open source (MIT License), hosted on GitHub. The single source of truth for what
is not yet done (and what has been deliberately deferred) lives in
docs/ROADMAP.md. For the current capability snapshot, see
CHANGELOG.md.

🤝 Contributing

Contributions are welcome! Please open an issue first to discuss design changes.

🙏 Acknowledgments

📄 License

MIT © 2026 aria contributors


📖 Alternative Formats

HTML Version · Chinese · Chinese HTML