【问题标题】:Drop a Rust void pointer stored in an FFI删除存储在 FFI 中的 Rust void 指针
【发布时间】:2016-07-10 06:44:16
【问题描述】:

我正在包装一个 C API,它允许调用者通过函数调用设置/获取任意指针。通过这种方式,C API 允许调用者将任意数据与 C API 对象之一相关联。该数据不用于任何回调,它只是一个指针,用户可以隐藏并稍后获取。

我的包装结构实现了包含此指针的 C 对象的 Drop 特征。我希望能够做的,但不确定是否可能,如果包装器结构下降时指针不为空,则数据是否正确下降。我不确定如何从原始 c_void 指针恢复正确的类型。

我想到的两个选择是

  1. 在包装器中实现这两个调用的行为。不要对 C API 进行任何调用。
  2. 不要试图为这些功能提供任何更安全的接口。说明指针必须由包装器的调用者管理。

我想做的事可能吗?如果没有,这种情况是否有普遍接受的做法?

【问题讨论】:

    标签: rust


    【解决方案1】:

    天真的 + 全自动方法是不可能的,原因如下:

    • 释放内存不会调用 drop/deconstructors/...: C API 可用于具有应正确解构的对象的语言,例如C++ 或 Rust 本身。因此,当您只存储一个内存指针时,您不知道要调用正确的函数(您既不知道哪个函数也不知道调用约定的样子)。

    • 哪个内存分配器?:内存分配和释放不是一件小事。您的程序需要向操作系统请求内存,然后以智能的方式管理这些资源,以确保高效和正确。这通常由图书馆完成。在 Rust 的情况下,使用 jemalloc(但 can be changed)。所以即使你要求 API 调用者只传递Plain Old Data(应该更容易破坏)你仍然不知道调用哪个库函数来释放内存。仅使用 libc::free 是行不通的(可以,但可能会严重失败)。

    解决方案:

    • dealloc 回调:您可以要求 API 用户设置一个额外的指针,例如 void destruct(void* ptr) 函数。如果这个不为 NULL,则在放置期间调用该函数。您还可以使用int 作为返回类型,以在销毁出错时发出信号。在这种情况下,您可以使用例如panic!

    • 全局回调:假设您要求您的用户仅传递 POD(普通旧数据)。要知道调用内存分配器的哪个free 函数,您可以要求用户注册一个全局void (*free)(void* ptr) 指针,该指针在drop 期间被调用。您也可以将其设为可选。

    【讨论】:

    • 在我问这个问题之前大约一个小时,我实际上已经想到了 dealloc 回调解决方案,但不知何故忘记了它。我要玩这个,看看我能走多远。
    【解决方案2】:

    虽然我能够遵循这个帖子中的建议,但我对我的结果并不完全满意,所以我在 Rust 论坛上提问并找到了我真正想要的 answer。 (play)

    use std::any::Any;
    
    static mut foreign_ptr: *mut () = 0 as *mut ();
    
    unsafe fn api_set_fp(ptr: *mut ()) {
        foreign_ptr = ptr;
    }
    
    unsafe fn api_get_fp() -> *mut() {
        foreign_ptr
    }
    
    struct ApiWrapper {}
    
    impl ApiWrapper {
        fn set_foreign<T: Any>(&mut self, value: Box<T>) {
            self.free_foreign();
            unsafe {
                let raw = Box::into_raw(Box::new(value as Box<Any>));
                api_set_fp(raw as *mut ());
            }
        }
    
        fn get_foreign_ref<T: Any>(&self) -> Option<&T> {
            unsafe {
                let raw = api_get_fp() as *const Box<Any>;
                if !raw.is_null() {
                    let b: &Box<Any> = &*raw;
                    b.downcast_ref()
                } else {
                    None
                }
            }
        }
    
        fn get_foreign_mut<T: Any>(&mut self) -> Option<&mut T> {
            unsafe {
                let raw = api_get_fp() as *mut Box<Any>;
                if !raw.is_null() {
                    let b: &mut Box<Any> = &mut *raw;
                    b.downcast_mut()
                } else {
                    None
                }
            }
        }
    
        fn free_foreign(&mut self) {
            unsafe {
                let raw = api_get_fp() as *mut Box<Any>;
                if !raw.is_null() {
                    Box::from_raw(raw);
                }
            }
        }
    }
    
    impl Drop for ApiWrapper {
        fn drop(&mut self) {
            self.free_foreign();
        }
    }
    
    struct MyData {
        i: i32,
    }
    
    impl Drop for MyData {
        fn drop(&mut self) {
            println!("Dropping MyData with value {}", self.i);
        }
    }
    
    fn main() {
        let p1 = Box::new(MyData {i: 1});
        let mut api = ApiWrapper{};
        api.set_foreign(p1);
        {
            let p2 = api.get_foreign_ref::<MyData>().unwrap();
            println!("i is {}", p2.i);
        }
        api.set_foreign(Box::new("Hello!"));
        {
            let p3 = api.get_foreign_ref::<&'static str>().unwrap();
            println!("payload is {}", p3);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 2017-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-26
      • 2019-02-03
      相关资源
      最近更新 更多