cxx icon indicating copy to clipboard operation
cxx copied to clipboard

Consider supporting closures/lambdas, not just function pointers

Open dtolnay opened this issue 5 years ago • 3 comments

Currently if we have:

mod ffi {
    extern "C++" {
        fn f(callback: fn());
    }
}

then Rust can call it as:

ffi::f(|| {
    do_something();
});

but not with any closure captures:

// not allowed

let x = /* ... */;
ffi::f(|| {
    do_something(x);
});

This gets very complicated from a lifetimes perspective, not to mention a FnOnce vs Fn perspective, so there is a good chance that we don't end up supporting this. But it is worth exploring.

The workaround for now is to pass context through manually via an opaque Rust type.

mod ffi {
    extern "Rust" {
        type CallbackContext;
    }

    extern "C++" {
        fn f(
            callback: fn(ctx: Box<CallbackContext>),
            ctx: Box<CallbackContext>,
        );
    }
}

let x = /* ... */;
ffi::f(
    |ctx| {
        do_something(ctx);
    },
    Box::new(x),
);

dtolnay avatar Apr 14 '20 00:04 dtolnay

I ran into this issue yesterday. Would be very useful to be able to pass a closure.

myronahn avatar Apr 14 '20 01:04 myronahn

Maybe at least the problem with lifetimes could be simplified by only accepting closures that have ownership of their contents (e.g. move || {}). That would still help in many cases, as you can then just pass Arcs around. Limiting to Fn for an initial implementation might also be enough.

So a minimum viable product could be accepting Box<impl Fn(...)->...>.

Which should at least allow you to do stuff like this:

let x = /* ... */;
ffi::f(move || {
    do_something(x);
});

LeonMatthesKDAB avatar Aug 26 '22 10:08 LeonMatthesKDAB