如何在 Rust 中正确包装 C 函数指针? [复制]

2023-12-26

我有一个 C 结构体Foo带有函数指针。在我的 Rust 绑定中,我希望允许用户设置此函数指针,但我希望避免用户必须处理 FFI 类型。

foo.h

struct Foo {
  void*   internal;
  uint8_t a;
  void (*cb_mutate_a)(void*);
};

struct Foo* foo_new();
void        foo_free(struct Foo* foo);
void        foo_call(struct Foo* foo);

foo.c

struct Foo* foo_new() {
  return calloc(1, sizeof(struct Foo));
}

void foo_free(struct Foo* foo) {
  free(foo);
}

void foo_call(struct Foo* foo) {
  return foo->cb_mutate_a(foo->internal);
}

我当前的解决方案是创建一个 Rust 结构Bar它有一个指向bindgen生成的C结构体的指针foo_sys::Foo,其中我有一个特征对象(rust_cb)这是我想在 Rust API 中公开的实际回调。我设置的是Ccb指向一个wrapped_cb并设置internal指向的指针Bar,这样我就可以打电话rust_cb从内部wrapped_cb.

该代码可以工作,但会抱怨访问未初始化的内存。当我用 Valgrind 运行它时,我看到invalid reads在我访问的地方(*bar).rust_cb inside wrapped_cb。我不确定我做错了什么。

extern crate libc;

use std::ffi;

#[repr(C)]
#[derive(Debug, Copy)]
pub struct Foo {
    pub internal: *mut libc::c_void,
    pub a: u8,
    pub cb_mutate_a: ::core::option::Option<unsafe extern "C" fn(arg1: *mut libc::c_void)>,
}

impl Clone for Foo {
    fn clone(&self) -> Self {
        *self
    }
}

extern "C" {
    pub fn foo_new() -> *mut Foo;
}
extern "C" {
    pub fn foo_free(foo: *mut Foo);
}
extern "C" {
    pub fn foo_call(foo: *mut Foo);
}

struct Bar {
    ptr: *mut Foo,
    rust_cb: Option<Box<dyn FnMut(&mut u8)>>,
}

impl Bar {
    fn new() -> Bar {
        unsafe {
            let mut bar = Bar {
                ptr: foo_new(),
                rust_cb: Some(Box::new(rust_cb)),
            };
            (*bar.ptr).cb_mutate_a = Some(cb);
            let bar_ptr: *mut ffi::c_void = &mut bar as *mut _ as *mut ffi::c_void;
            (*bar.ptr).internal = bar_ptr;
            bar
        }
    }
}

impl Drop for Bar {
    fn drop(&mut self) {
        unsafe {
            foo_free(self.ptr);
        }
    }
}

extern "C" fn cb(ptr: *mut libc::c_void) {
    let bar = ptr as *mut _ as *mut Bar;
    unsafe {
        match &mut (*bar).rust_cb {
            None => panic!("Missing callback!"),
            Some(cb) => (*cb)(&mut (*(*bar).ptr).a),
        }
    }
}

fn rust_cb(a: &mut u8) {
    *a += 2;
}

fn main() {
    unsafe {
        let bar = Bar::new();
        let _ = foo_call(bar.ptr);
    }
}

我查看了相关问题,这些问题似乎回答了我的问题,但解决了不同的问题:

  • Rust FFI 将特征对象作为上下文传递以调用回调 https://stackoverflow.com/q/33929079/155423

这使用dlsym从 C 调用 Rust 回调。

  • 如何将 Rust 闭包转换为 C 风格回调? https://stackoverflow.com/q/32270030/155423
  • 如何通过原始指针将闭包作为参数传递给 C 函数? https://stackoverflow.com/q/38995701/155423

这些描述了将闭包作为 C 函数指针传递的解决方案。

我想要实现的是拥有一个 Rust 结构(Bar) 有一个成员变量ptr指向一个 C 结构体(Foo),它本身有一个void *internal指向 Rust 结构体Bar.

这个想法是在 Rust 结构中拥有一个特征对象和包装函数BarC 结构体中的每个函数指针Foo。当一个Bar创建对象后,我们执行以下操作:

  • 创建CFoo并保留一个指向它的指针Bar.
  • Point Foo->callback包装 Rust 函数。
  • Point Foo->internal to Bar.

由于包装函数传递了internal指针,我们可以得到一个指向Bar并调用相应的闭包(来自特征 obj)。

我能指出Cvoid*到我的 Rust 结构,我还可以从 Rust 回调(或闭包)获取指向它的指针,这就是相关问题所解决的问题。问题我am面临的问题可能与生命周期有关,因为其中一个值的生命周期不够长,无法在回调中使用。


这是一个错误(由@Shepmaster识别)Bar::new()函数,由于我对 Rust 移动语义的根本误解而引起。通过具有固定Bar::new()返回一个Box<Bar> -

impl Bar {
    fn new() -> Box<Bar> {
        unsafe {
            let mut bar = Box::new(Bar { ptr: foo_sys::foo_new(), rust_cb: Some(Box::new(rust_cb)) });
            (*bar.ptr).cb_mutate_a = Some(cb);
            let bar_ptr: *mut ffi::c_void = &mut *bar as *mut _ as *mut ffi::c_void;
            (*bar.ptr).internal = bar_ptr;
            bar
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Rust 中正确包装 C 函数指针? [复制] 的相关文章

随机推荐

  • 如何在 C# 中读取 .xls 和 .xlsx 和 .xlsm 文件

    我使用 openfiledialog 并过滤它以获取 xls 和 xlsx 和 xlsm 文件 但我不知道下一步该做什么 我构建了一个具有名字和姓氏的工人类 我想从 Excel 文件中获取数据并将其放入变量中 这是我的 openfiledi
  • 如何在Golang中使用GORM for Mongodb?

    我是新来的go and MongoDB 我想使用 GORM 连接到 MongoDBgo lang 经过大量搜索 我仍然无法做到 简而言之 你不能 GORM http gorm io MongoDB 是为关系型数据库而创建的 MongoDB
  • Internet Explorer 中的 Go 和 Gorilla 会话

    我正在使用 Go 制作一个简单的 Web 应用程序 使用 gorilla 进行会话和路由 使用 Mustache 进行模板 我在登录时遇到问题 我相信是 IE 接受 cookie 的问题 该问题仅出现在 Internet Explorer
  • 错误:无法在截止日期前连接 URL:grpc://localhost:7051

    Documents test education LFS171x fabric material tuna app node query js Store path home Documents test education LFS171x
  • JPA 和 Hibernate Cascade DELETE OneToMany 不起作用

    我一直在阅读一篇又一篇的文章 试图让级联删除与最新的 Spring Boot 版本中的 JPA Hibernate 一起使用 我读过您必须使用 Hibernate 特定的级联 但我读过您不需要 我读到它们只是不起作用 但它似乎是一个混合包
  • Android Studio导入项目错误

    我在网上下载了一些示例 Android 项目 导入 android studio 但不起作用 举个例子http developer android com training implementing navigation terior ht
  • JPA 2 Criteria API:为什么 isNull 在与 equal 结合使用时被忽略?

    我有以下实体类 继承自持久对象支持类 Entity public class AmbulanceDeactivation extends PersistentObjectSupport implements Serializable pri
  • Chrome 扩展 - 未捕获的 ReferenceError:$ 未定义

    我使用 chrome tabs API 在每次更新选项卡时运行脚本 该脚本在页面中搜索关键字 如果找到 它会提醒您 但如果没有 它会刷新页面 每当我测试扩展时 控制台都会告诉我 清单 json name keyword checker ve
  • 将 excel 时间导入 Pandas(带小数秒)

    我有一个包含时间列的 Excel 电子表格 xls 时间在 Excel 中显示为分钟 秒 十分之一秒 例如 50 59 2 50 59 4 原始数据包含小时 分钟 秒 十进制秒 当我将数据导入 pandas 时 我损失了十分之一秒 inda
  • Android 导航组件活动意图标志

    我已经创建了从片段到活动的导航操作 但我无法清除后堆栈 当我执行从片段到新活动的导航操作并按下后退按钮时 我将返回到上一个活动和上一个片段 我无法使用导航图设置意图标志来从返回堆栈中清除先前的活动
  • 从组件的“style”属性中获取 CSS 属性值

    我正在为一个库编写一个 React Native 组件 我希望用户能够使用style财产 就像React View和其他内置组件 但是 由于我的组件实际上是由一些嵌套视图组成的 因此我需要进行一些计算来确定要在组件上放置什么样式inner那
  • 使用帧检测标记的增强现实视频播放

    我想在使用网络摄像头检测到标记 HIRO 时播放视频 当我删除它时 它应该暂停 当检测到标记时 视频应该使用 A 框架播放 我已经编写了代码 但它不起作用 谁能帮我 我尝试了所有可能的方法 但它不起作用 所以任何人都可以发布代码或发送示例
  • C# 或 VB.NET - 迭代所有公共枚举

    我们的源代码中有一个通用组件 其中包含非常大的应用程序的所有枚举 大约 300 个 有没有什么方法可以使用 C 或 VB NET 来遍历所有这些 以便对每一个执行操作 问题如何迭代 net 类中的所有 公共字符串 属性 https stac
  • Azure 逻辑应用、SQL 更新行 v2

    我正在尝试添加一个额外的任务来在通过 sendgrid 发送电子邮件后更新 SQL Server 中的行 用于更新行的选项似乎缺少列名称和值 唯一可以添加的附加参数称为 原始输入 该参数不在 MS 文档中 完成可用字段并运行该进程 它可以正
  • 如何在无线设置中添加 return=true [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在做一个项目 但我卡住了 我想要的是启用 wifi 或互联网后 后退按钮 硬件按钮 应该让我回到SplitScreen xml不要关闭应
  • 使用 ndk-build 构建 android 共享库,无需 jni 文件夹

    仅当我的所有 src 都在 jni foler 中时 我才能使用 ndk build 构建一个 android 共享库 但是 我想使用 ndk build 构建一个共享库而不需要 jni 文件夹 因为我的项目没有java代码 那么我该怎么做
  • 字典为相同的键添加值

    我有一个字典列表 name Jay value 1 name roc value 9 name Jay value 7 name roc value 2 我希望它是 name Jay value 8 name roc value 11 我尝
  • 从Artifactory下载依赖项时npm错误403

    我正在尝试使用 Node js 项目运行作业 步骤之一是rtnpminstall它运行npm install命令 这rtnpminstall命令由 Jenkins 上的 Artifactory 插件运行 错误说 npm ERR code E
  • 如何在 Swift 中以编程方式将 HeaderView 从 nib 添加到 UiTableView

    好吧 我是一个使用 swift 语言的天真的 IOS 开发人员 我有一个显示酒店功能列表的表格视图 现在我想在表格视图中添加标题信息 其中包含酒店图像 酒店名称位于表格视图上方 以便标题信息也随着表格视图内容滚动 产品功能列表 问题是 带有
  • 如何在 Rust 中正确包装 C 函数指针? [复制]

    这个问题在这里已经有答案了 我有一个 C 结构体Foo带有函数指针 在我的 Rust 绑定中 我希望允许用户设置此函数指针 但我希望避免用户必须处理 FFI 类型 foo h struct Foo void internal uint8 t