bytecodealliance / bytecodealliance/wit-bindgen

CPP: async guest bindings sketch

Open
#1,426 9 comments 1 reaction 0 assignees View on GitHub
gen-cpp
Dominant language
Rust
Stars
1.5k
Forks
286
Avg merge
6h 32m
Merged PRs (30d)
19

Description

While we could go with exactly the choices made for async c bindgen, I'd like to sketch a C++ native solution.

C++20 introduces [coroutines](https://en.cppreference.com/w/cpp/language/coroutines.html) which are a much closer fit to the co-operative threads design in the component model.

The perennial challenge with C++ is which minimum version should we support? I see where we lean into C++ 17 features today, but could/should we raise the bar to C++20? I started sketching what it might look like assuming we could depend on C++20 and also assuming we wouldn't go to C++23.

cc @cpetig

Some basic design decisions:
- Should we use exceptions for async failures? These appear to be uncommon in modern C++ for perf reasons and we should use `std::expected` instead.
- Move semantics for all async operations, and have them only use owned values (to prevent use-after-move)
- No references/borrows in async contexts (to prevent dangling)
- All operations are cancellable
- RAII for automatic handle cleanup

Just a heads up that I'm knocking some rust off (pun intended) after having not written C++ professionally in several years. So please don't take my proposal here as authoritative as there's a solid chance I've missed some features we may want to take advantage of.

## Usage example

```wit
interface example {
get-value: async func() -> u32;
}
```

```cpp
// Bindings
namespace example {
// Async import
Task GetValue();

// Implementation (export)
namespace exports {
uint32_t GetValue() {
// User implementation
return 42;
}
}
}

// Usage
Task example_task = example::GetValue();
uint32_t value = co_await example_task;
```

## Sketches on implementation details

```cpp
// Cancellation support
auto cancel() -> std::optional {
uint32_t status = writer_->vtable_->cancel_write(writer_->handle_);
if (status == (uint32_t)AsyncStatus::Cancelled) {
// Successfully cancelled, return original value
return std::move(value_);
}
return std::nullopt; // Already completed
}
```

Sketch for reading values from a stream:
```cpp
// Read one value (returns nullopt on EOF)
auto next() -> Task> {
T value;
auto result = co_await read(std::span(&value, 1));
if (result.count == 0) {
co_return std::nullopt;
}
co_return std::move(value);
}

// Collect all remaining values
auto collect() -> Task> {
std::vector results;
while (auto val = co_await next()) {
results.push_back(std::move(*val));
}
co_return results;
}
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.