Basic Example — C++
Source: apps/cpp/example/src/app.cpp | WASM Demo
The C++ example demonstrates the ove:: namespace wrappers with RAII semantics, template-based objects, Result<T> error handling, and the LVGL C++20 fluent builders.
The C++ binding builds with -std=c++23 across all backends (POSIX, FreeRTOS, NuttX, Zephyr).
C++ RAII patterns
Objects are declared at file scope — no _create() / _destroy() calls needed:
static ove::Queue<uint32_t, 8> counter_queue;
static ove::Mutex value_mutex;
static ove::Thread<4096> prod_thread(producer_thread, nullptr,
OVE_PRIO_NORMAL, "producer");
static ove::Thread<4096> cons_thread(consumer_thread, nullptr,
OVE_PRIO_NORMAL, "consumer");
ove::Queue<T, N> is a fixed-size typed queue (compile-time enforced std::is_trivially_copyable_v<T>). ove::Thread<StackSize> auto-allocates its stack at compile time. ove::Mutex wraps ove_mutex_t with RAII.
Result<T> and Error
Fallible operations across the binding return ove::Result<T> — an alias for std::expected<T, ove::Error> — instead of raw int rc-codes. The error side is a typed enum class ove::Error mirroring every substrate OVE_ERR_* constant. Both can flow through std::error_code via the bundled category.
// Forever-blocking forms assert on failure (programming error):
mtx.lock(); // no return; aborts on substrate failure
queue.send(item); // forever-wait variant
queue.receive(out);
// Bounded forms return Result<void>:
if (auto r = mtx.try_lock_for(100ms); r) {
/* got the lock */
} else if (r.error() == ove::Error::Timeout) {
/* deadline elapsed */
}
// Methods that produce a value return Result<T>:
auto r = sock.send(buf, len); // Result<size_t>
if (r && *r == len) { /* fully sent */ }
auto resp = http_client.get(url); // Result<Response>
if (resp) { use(resp->status(), resp->body()); }
Producer thread
static void producer_thread(void *)
{
uint32_t count = 0;
OVE_LOG_INF("Producer started");
using namespace std::chrono_literals;
while (true) {
++count;
if (!counter_queue.try_send_for(count, 1000ms)) {
OVE_LOG_WRN("Producer: queue full, dropped %u", count);
}
ove::this_thread::sleep_ms(500);
}
}
try_send_for(item, duration) returns Result<void> — operator bool is has_value(), so if (!result) reads as "send didn't succeed." Use result.error() to discriminate Error::Timeout vs Error::QueueFull if needed.
ove::this_thread::sleep_ms() mirrors std::this_thread::sleep_for shape (the namespace also exposes sleep_for(duration), yield(), get_id(), self()).
Composing with the standard library
ove::Mutex satisfies the BasicLockable and Lockable named requirements, so it composes directly with std::lock_guard and std::scoped_lock:
ove::Mutex a, b;
{
std::lock_guard<ove::Mutex> g(a);
/* a is held */
}
{
std::scoped_lock g(a, b); // deadlock-avoidance dance
/* both held */
}
stop_token / stop_source mirror std::stop_token / std::stop_source shape for cooperative thread cancellation; capturing-lambda constructors mirror std::jthread.
LVGL UI
The C++ example uses the LVGL C++20 wrapper with fluent builders:
namespace lv = ove::lvgl;
lv::LvglGuard guard;
auto scr = lv::ObjectView::screen_active();
lv_obj_set_style_bg_color(scr, lv_color_black(), 0);
lv::Label::create(scr)
.text(APP_TITLE)
.font(&lv_font_montserrat_32)
.color(lv_color_white())
.align(LV_ALIGN_TOP_MID, 0, 16);
lv::LvglGuard is an RAII lock — it calls ove_lvgl_lock() on construction and ove_lvgl_unlock() on destruction. The fluent setters drop the LVGL set_ prefix (text, font, color, radius, …) for terser chains.
Entry point
OVE_MAIN()
{
OVE_LOG_INF("C++ example: init");
/* Objects are auto-created at file scope via constructors.
* Just init LVGL and start the scheduler. */
ove::lvgl::init();
{ lv::LvglGuard g; create_ui(); }
if (auto r = ui_timer.start(); !r) {
OVE_LOG_ERR("UI timer start failed: %d", static_cast<int>(r.error()));
return;
}
ove::run();
}
No manual _create() / _destroy() — C++ constructors and destructors handle lifecycle.
Key APIs demonstrated
| C++ API | C Equivalent | Purpose |
|---|---|---|
ove::Queue<T, N> |
ove_queue_create |
Typed fixed-size queue, Result<void> returns |
ove::Mutex |
ove_mutex_create |
RAII mutex, satisfies std::Lockable |
ove::Thread<N> |
ove_thread_create |
Thread with compile-time stack, jthread-style cancel |
ove::Timer |
ove_timer_create |
Periodic callback timer |
ove::this_thread::* |
ove_thread_yield/sleep_ms |
std::this_thread-shaped helpers |
ove::Result<T> |
— | std::expected<T, ove::Error> for fallible ops |
lv::LvglGuard |
ove_lvgl_lock/unlock |
RAII LVGL locking |
lv::Label::create() |
lv_label_create |
Fluent widget builder |
ove::run() |
ove_run |
Start scheduler |
How to build
make host.posix.example_cpp_heap # or wasm.posix.example_cpp_heap
make configure && make download && make && make run