Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for std::function #1100

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

paandahl
Copy link
Contributor

@paandahl paandahl commented Sep 21, 2022

I put this up as a draft to get feedback; the code still needs cleanup / documentation.

I have added support for std::function, with the following interface:

#[cxx::bridge]
pub mod ffi {
    pub struct CallbackArgs<'a>(
        u8,
        &'a Vec<u8>,
        Box<OpaqueStruct>,
    );
    
    extern "Rust" {
        type OpaqueStruct;
        unsafe fn t_take_ref_func_single_arg<'a>(f: &CxxFunction<&'a OpaqueStruct, ()>);
        fn t_take_ref_func_multi_arg(f: &CxxFunction<CallbackArgs, u16>);
    }
}

unsafe fn t_take_ref_func_single_arg<'a>(f: &CxxFunction<&'a OpaqueStruct, ()>) {
    f.call(&OpaqueStruct { ... });
}

fn t_take_ref_func_multi_arg(f: &CxxFunction<ffi::CallbackArgs, u16>) {
    let ret = f.call(
        ffi::TestArgs(
            2,
            vec![42],
            Box::new(OpaqueStruct { ... }),
        )
    );
}

Usage from C++:

t_take_ref_func_single_arg([&](const OpaqueStruct& arg) {
    ...
});

t_take_ref_func_multi_arg([&](
    uint8_t arg,
    rust::Vec<uint8_t> arg2,
    rust::Box<OpaqueStruct> arg3
) {
    ...
    return (uint16_t)200;
});

Explanation

The CxxFunction<A, R> takes two template arguments.

  • A - the arguments. Can be one of:
    1. A shared or opaque Rust type that is declared in the crate
    2. A tuple struct (most internal types supported)
  • R - the return type.

If A is a tuple struct, the contents will be spread out, so that each element will match a function argument. The tuple struct itself evaporates at the language boundary.

The callback can also be passed by value, wrapped in a UniquePtr.

Rationale

I wanted the API to resemble those of CxxVector, UniquePtr, etc. The tuple struct is not ideal, but necessary to allow for multiple arguments.

I considered looking into a wrapper that would expose the callback as a Fn trait object, but that seems out of line with the rest of the API.

Possible improvements

  • The code can probably be simplified / cleaned up a bit more
  • Some things are skipped for now - checking-logic, documentation, etc.

Discussion

How was this functionality imagined? Is this the way to go, or are there better ways to solve the problem at hand?

Closes: #52

@casimcdaniels
Copy link

This is impeccable timing as I was just trying to implement C++ callbacks called from Rust.

@kdewald
Copy link

kdewald commented Dec 26, 2022

Hey all! Is there a status update on this feature?

@noufelez
Copy link

Hello everyone, is this feature going to be merged. ? :) @dtolnay @paandahl

We are looking into CXX at https://easymile.com/ and we'd love to use it and possibly contribute if there are things we can do.

Thanks!

@owenshoemaker
Copy link

It has been a few months and just wanted to check if there was any update. This would be super helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support passing function pointers across the FFI
5 participants