bytecodealliance / bytecodealliance/wit-bindgen
CPP: async guest bindings sketch
- 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.