1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use std::ffi::c_void;

/// Splits a closure into its data part and its code part, allowing it to be
/// used as a callback by FFI code.
///
/// # Examples
///
/// ```rust
/// use std::ffi::c_void;
///
/// let mut total = 0;
///
/// // let's define a closure which will update a total and return its new value
/// let mut some_closure = |n: usize| { total += n; total };
///
/// // the callback the C function is expecting
/// type Callback = unsafe extern "C" fn(*mut c_void, usize) -> usize;
///
/// // pretend this is some C function which will periodically call a callback,
/// // passing along a user-provided pointer for state.
/// unsafe fn some_c_function(max_value: usize, cb: Callback, user_data: *mut c_void) {
///     for i in 0..max_value {
///         let got = cb(user_data, i);
///         println!("iteration: {}, total: {}", i, got);
///     }
/// }
///
/// unsafe {
///     // split the closure into its state section and the code section
///     let (state, callback) = ffi_helpers::split_closure(&mut some_closure);
///
///     // then pass it to the C function
///     some_c_function(42, callback, state);
/// }
///
/// assert_eq!(total, (0..42).sum());
/// ```
///
/// # Safety
///
/// The returned function can only be called with the returned pointer, or a
/// pointer to another `C` closure.
pub unsafe fn split_closure<C, Args, Ret>(
    closure: &mut C,
) -> (*mut c_void, C::Trampoline)
where
    C: Split<Args, Ret>,
{
    (closure as *mut C as *mut c_void, C::trampoline())
}

/// A helper trait used by [`split_closure()`] to get a trampoline function
/// which will invoke the closure.
///
/// This trait is automatically implemented for any `FnMut()` callable, you
/// shouldn't implement it yourself.
pub trait Split<Args, Ret> {
    type Trampoline;

    fn trampoline() -> Self::Trampoline;
}

macro_rules! impl_split {
    ($( $outer:ident ),* ; $( $inner:ident ),*) => {
        impl<Func, Ret, $($outer),*> Split<($( $outer, )*), Ret> for Func
        where
            Func: FnMut($($outer),*) -> Ret,
        {
            type Trampoline = unsafe extern "C" fn(*mut c_void, $($outer),*) -> Ret;

            fn trampoline() -> Self::Trampoline {
                // declare a trampoline function which will turn our pointer
                // back into an `F` and invoke it

                // Note: we're deliberately using `$inner` to generate an ident
                // for the argument
                #[allow(non_snake_case)]
                unsafe extern "C" fn trampoline<T, Ret_, $( $inner ),*>(ptr: *mut c_void, $($inner: $inner),*) -> Ret_
                where
                    T: FnMut($($inner),*) -> Ret_,
                {
                    debug_assert!(!ptr.is_null());

                    let callback: &mut T = &mut *(ptr as *mut T);

                    callback($($inner),*)
                }

                trampoline::<Func, Ret, $($outer,)*>
            }
        }
    };
}

impl_split!(;);
impl_split!(A; A);
impl_split!(A, B; A, B);
impl_split!(A, B, C; A, B, C);
impl_split!(A, B, C, D; A, B, C, D);
impl_split!(A, B, C, D, E; A, B, C, D, E);
impl_split!(A, B, C, D, E, F; A, B, C, D, E, F);
impl_split!(A, B, C, D, E, F, G; A, B, C, D, E, F, G);
impl_split!(A, B, C, D, E, F, G, H; A, B, C, D, E, F, G, H);
impl_split!(A, B, C, D, E, F, G, H, I; A, B, C, D, E, F, G, H, I);
impl_split!(A, B, C, D, E, F, G, H, I, K; A, B, C, D, E, F, G, H, I, K);
impl_split!(A, B, C, D, E, F, G, H, I, K, L; A, B, C, D, E, F, G, H, I, K, L);
impl_split!(A, B, C, D, E, F, G, H, I, K, L, M; A, B, C, D, E, F, G, H, I, K, L, M);
impl_split!(A, B, C, D, E, F, G, H, I, K, L, M, N; A, B, C, D, E, F, G, H, I, K, L, M, N);
impl_split!(A, B, C, D, E, F, G, H, I, K, L, M, N, O; A, B, C, D, E, F, G, H, I, K, L, M, N, O);