Simple interoperability between C++ coroutines and asynchronous Rust

Related tags

Utilities cxx-async
Overview

cxx-async

Overview

cxx-async is a Rust crate that extends the cxx library to provide seamless interoperability between asynchronous Rust code using async/await and C++20 coroutines using co_await. If your C++ code is asynchronous, cxx-async can provide a more convenient, and potentially more efficient, alternative to callbacks. You can freely convert between C++ coroutines and Rust futures and await one from the other.

It's important to emphasize what cxx-async isn't: it isn't a C++ binding to Tokio or any other Rust I/O library. Nor is it a Rust binding to boost::asio or similar. Such bindings could in principle be layered on top of cxx-async if desired, but this crate doesn't provide them out of the box. (Note that this is a tricky problem even in theory, since Rust async I/O code is generally tightly coupled to a single library such as Tokio, in much the same way C++ async I/O code tends to be tightly coupled to libraries like boost::asio.) If you're writing server code, you can still use cxx-async, but you will need to ensure that both the Rust and C++ sides run separate I/O executors.

cxx-async aims for compatibility with popular C++ coroutine support libraries. Right now, both the lightweight cppcoro and the more comprehensive Folly are supported. Pull requests are welcome to support others.

Quick tutorial

To use cxx-async, first start by adding cxx to your project. Then add the following to your Cargo.toml:

[dependencies]
cxx-async = "0.1"

Now, inside your #[cxx::bridge] module, declare a future type and some methods like so:

Box ; } // Async Rust methods that you wish C++ to call go here. Again, make sure they return one of the // boxed future types you declared above. extern "Rust" { fn hello_from_rust() -> Box ; } } ">
#[cxx::bridge]
mod ffi {
    // Give each future type that you want to bridge a name.
    extern "Rust" {
        type RustFutureString;
    }

    // Async C++ methods that you wish Rust to call go here. Make sure they return one of the boxed
    // future types you declared above.
    unsafe extern "C++" {
        fn hello_from_cpp() -> Box
     ;
    }

    
     // Async Rust methods that you wish C++ to call go here. Again, make sure they return one of the
    
     // boxed future types you declared above.
    
     extern 
     "Rust" {
        
     fn 
     hello_from_rust() -> 
     Box
     
      ;
    }
}
     
    

After the #[cxx::bridge] block, define the future types using the #[cxx_async::bridge_future] attribute:

// The inner type is the Rust type that this future yields.
#[cxx_async::bridge_future]
struct RustFutureString(String);

Now, in your C++ file, make sure to #include the right headers:

#include "rust/cxx.h"
#include "rust/cxx_async.h"
#include "rust/cxx_async_cppcoro.h"  // Or cxx_async_folly.h, as appropriate.

And add a call to the CXXASYNC_DEFINE_FUTURE macro to define the C++ side of the future:

// The first argument is the name you gave the future, and the second argument is the corresponding
// C++ type. The latter is the C++ type that `cxx` maps your Rust type to: in this case, `String`
// maps to `rust::String`, so we supply `rust::String` here.
CXXASYNC_DEFINE_FUTURE(RustFutureString, rust::String);

You're all set! Now you can define asynchronous C++ code that Rust can call:

rust::Box 
   hello_from_cpp() {
    
   co_return 
   std::string(
   "Hello world!");
}
  

On the Rust side:

async fn call_cpp() -> String {
    // This returns a Result (with the error variant populated if C++ threw an exception), so you
    // need to unwrap it:
    ffi::hello_from_cpp().await.unwrap()
}

And likewise, define some asynchronous Rust code that C++ can call:

use cxx_async::CxxAsyncResult;
fn hello_from_rust() -> Box
    {
    
   // You can instead use `fallible` if your async block returns a Result.
    RustFutureString
   ::
   infallible(
   async { 
   "Hello world!".
   to_owned() })
}
  

Over on the C++ side:

cppcoro::task 
   call_rust() {
    
   co_return 
   hello_from_rust();
}
  

That's it! You should now be able to freely await futures on either side.

Code of Conduct

cxx-async follows the same Code of Conduct as Rust itself. Reports can be made to the crate authors.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

You might also like...
my attempt at compromise between unwrapping and bullying my dependencies' authors for Error impl

string-eyre Has this happened to you? error[E0599]: the method `wrap_err` exists for enum `Result(), tauri::Error`, but its trait bounds were not sa

Count and convert between different indexing schemes on utf8 string slices

Str Indices Count and convert between different indexing schemes on utf8 string slices. The following schemes are currently supported: Chars (or "Unic

A translator interface between VoiceMeeter/VBAN and the X-Touch

VBAN - XCtrl translator This serves as a network based translator for interfacing the Behringer X-Touch with VoiceMeeter Potato. It is designed to ru

Create virtual serial ports, connect them to physical serial ports, and create routes between them all.

Virtual Serial Port Router (vsp-router) Create virtual serial ports, connect them to physical serial ports, and create routes between them all. vsp-ro

Get a diff between two OpenAPI descriptions.

Get the difference between two OpenAPI descriptions.

A compatibility layer to smooth the transition between different versions of embedded-hal

Embedded HAL Compatibility Layer A compatibility layer to smooth the transition between different versions of embedded-hal (specifically 0.2.x and 1.0

Easily sync your clipboard between devices. This is a work in progress app.

Clipboard Sync Description Easily sync your clipboard between devices. This is a work in progress app. Stack Frontend: React Tauri isomorphic-ws TSX,

Generate enum from a trait, with converters between them
Generate enum from a trait, with converters between them

Derive macro for Rust that turns traits into enums, providing tools for calling funtions over channels

Stdto provides a set of functional traits for conversion between various data representations.

Stdto stdto provides a set of functional traits for conversion between various data representations. | Examples | Docs | Latest Note | stdto = "0.13.0

Comments
  • C++ compilation error when returning `RustFutureVoid` from Rust to C++

    C++ compilation error when returning `RustFutureVoid` from Rust to C++

    I have the following defined:

    mod ffi {
        unsafe extern "C++" {
            include!("cxx_rs_future_defs.h");
            
            type RustFutureVoid = super::RustFutureVoid;
        }
    
        extern "Rust" {
            fn foo() -> RustFutureVoid;
        }
    }
    
    unsafe impl Future for RustFutureVoid {
        type Output = ();
    }
    
    pub fn foo() -> RustFutureVoid {
        RustFutureVoid::fallible(async { Ok(()) })
    }
    

    with cxx_rs_future_defs.h:

    #pragma once
    
    #define CXXASYNC_HAVE_COROUTINE_HEADER
    
    #include "cxx-async/include/rust/cxx_async.h"
    #include "cxx-async/include/rust/cxx_async_folly.h"
    #include "rust/cxx.h"
    
    CXXASYNC_DEFINE_FUTURE(
        void,
        RustFutureVoid);
    

    and C++ code that calls this Rust method, e.g.

    folly::coro::Task<void> calls_foo() {
        co_await foo();
    }
    

    however this does not compile due to the following:

    cxx-async/include/rust/cxx_async.h:590:14: error: cannot form a reference to 'void'
      YieldResult&& await_resume() {
                 ^
    folly/experimental/coro/Traits.h:78:36: note: in instantiation of template class 'rust::async::RustAwaiter<RustFutureVoid>' requested here
            decltype(std::declval<T&>().await_ready()),
    ...
    

    seems to be a unique case with void specifically?

    opened by emersonford 1
Owner
Patrick Walton
Patrick Walton
prelate-rs is an idiomatic, asynchronous Rust wrapper around the aoe4world API. Very much a WIP at this stage.

prelate-rs is an idiomatic, asynchronous Rust wrapper around the aoe4world API. Very much a WIP at this stage. Project Status We currently support the

William Findlay 4 Dec 29, 2022
An asynchronous Hardware Abstraction Layer (HAL) for embedded systems

embedded-hal-async An asynchronous Hardware Abstraction Layer (HAL) for embedded systems. This crate contains asynchronous versions of the embedded-ha

Diego Barrios Romero 3 Jan 22, 2022
An asynchronous IO utilities crate powered by tokio.

An asynchronous IO utilities crate powered by tokio.

Harry 2 Aug 18, 2022
dark-std an Implementation of asynchronous containers build on tokio

dark-std dark-std is an Implementation of asynchronous containers build on tokio. It uses a read-write separation design borrowed from Golang SyncHash

darkrpc 4 Dec 13, 2022
Rate limit guard - Lazy rate limit semaphore implementation to control your asynchronous code frequency execution

Lazy rate limit semaphore (a.k.a fixed window algorithm without queueing) implementation to control your asynchronous code frequency execution

Yan Kurbatov 4 Aug 1, 2022
Various extention traits for providing asynchronous higher-order functions

async-hofs Various extention traits for providing asynchronous higher-order functions. // This won't make any name conflicts since all imports inside

かわえもん 5 Jun 28, 2022
Asynchronous runtime abstractions for implicit function decoloring.

decolor Asynchronous runtime abstractions for implicit function decoloring. Decolor is in beta Install | User Docs | Crate Docs | Reference | Contribu

refcell.eth 11 Oct 26, 2023
A lean, minimal, and stable set of types for color interoperation between crates in Rust.

This library provides a lean, minimal, and stable set of types for color interoperation between crates in Rust. Its goal is to serve the same function that mint provides for (linear algebra) math types.

Gray Olson 16 Sep 21, 2022
Easy switch between AWS Profiles and Regions

AWSP - CLI To Manage your AWS Profiles! AWSP provides an interactive terminal to interact with your AWS Profiles. The aim of this project is to make i

KubeOps Skills 14 Dec 25, 2022
This crate bridges between gstreamer and tracing ecosystems.

This crate provides a bridge between gstreamer and the tracing ecosystem. The goal is to allow Rust applications utilizing GStreamer to better integra

Standard Cognition OSS 17 Jun 7, 2022