Безопасный указатель Fn со стиранием типа в ржавчине, который можно использовать для вызова связанной и обычной функции

Я реализую метод обратного вызова, чтобы заменить использование дженериков.

Для максимальной производительности нельзя использовать Box и я также не хочу использовать dyn FnMut(T) -> Ret как это вводит vtable.

Поэтому я решил написать не принадлежащий ErasedFnPointer в котором хранится указатель функции и указатель на struct если указатель функции является связанной функцией.

Это не так уж и сложно, поскольку связанная функция в ржавчине — это просто обычная функция.

Чтобы сделать его меньше, я использую transmute конвертировать fn(&self, T) -> Ret к fn(*mut c_void, T) -> Ret, который работает, однако я не уверен в его правильности.

Мне также интересно, существует ли другой подход, более производительный и компактный.

use core::ffi::c_void;
use core::mem::transmute;
use core::ptr::null_mut;
use core::marker::PhantomData;

/// ErasedFnPointer can either points to a free function or associated one that
/// `&mut self`
struct ErasedFnPointer<'a, T, Ret> {
    struct_pointer: *mut c_void,
    fp: *const (),
    // The `phantom_*` field is used so that the compiler won't complain about
    // unused generic parameter.
    phantom_sp: PhantomData<&'a ()>,
    phantom_fp: PhantomData<fn(T) -> Ret>,
}

impl<'a, T, Ret> ErasedFnPointer<'a, T, Ret> {
    pub fn from_associated<S>(struct_pointer: &'a mut S, fp: fn(&mut S, T) -> Ret)
        -> ErasedFnPointer<'a, T, Ret>
    {
        ErasedFnPointer {
            struct_pointer: struct_pointer as *mut _ as *mut c_void,
            fp: fp as *const (),
            phantom_sp: PhantomData,
            phantom_fp: PhantomData,
        }
    }
    
    pub fn from_free(fp: fn(T) -> Ret) -> ErasedFnPointer<'static, T, Ret> {
        ErasedFnPointer {
            struct_pointer: null_mut(),
            fp: fp as *const (),
            phantom_sp: PhantomData,
            phantom_fp: PhantomData,
        }
    }
    
    pub fn call(&self, param: T) -> Ret {
        if self.struct_pointer.is_null() {
            let fp = unsafe { transmute::<_, fn(T) -> Ret>(self.fp) };
            fp(param)
        } else {
            let fp = unsafe { transmute::<_, fn(*mut c_void, T) -> Ret>(self.fp) };
            fp(self.struct_pointer, param)
        }
    }
}

fn main() {
    let erased_ptr = ErasedFnPointer::from_free(|x| {
        println!("Hello, {}", x);
        x
    });
    erased_ptr.call(2333);
    
    println!("size_of_val(erased_ptr) = {}", core::mem::size_of_val(&erased_ptr));

    ErasedFnPointer::from_associated(
        &mut Test { x: 1},
        Test::f
    ).call(1);
    
    let mut x = None;
    ErasedFnPointer::from_associated(&mut x, |x, param| {
        *x = Some(param);
        println!("{:#?}", x);
    }).call(1);
}

struct Test {
    x: i32
}
impl Test {
    fn f(&mut self, y: i32) -> i32 {
        let z = self.x + y;
        println!("Hello from Test, {}", z);
        z
    }
}
```

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *