4

我有一个使用 Berkeley DB 作为存储后端的现有 C++ 程序。我想用 Rust 重写它。有没有办法在 Rust 中编写一个外部函数接口来使用 Berkeley DB?我找到了教程Rust Foreign Function Interface,但对于 BDB 中使用的复杂 C 结构来说,这似乎太简单了;例如,要打开一个数据库,我需要声明一个 DB 结构并调用DB->open(). 但我不知道如何使用教程中显示的示例来做到这一点。

有人能帮忙吗?

4

2 回答 2

3

好吧,查看 BDB 的 C API,我发现它由 C 结构和指向函数的元素指针组成。教程中没有解释(这很奇怪),但 Rust 目前支持指向外部函数的指针。Rust 参考手册中也提到了它。

您可以根据定义的结构大致创建所有必需的结构db.h,并且由于 Rust 和 C 结构的内存布局是相同的,您可以将这些结构传入/传出库,并期望它们中存在正确的指针。

例如,您的DB->open()电话可能如下所示:

struct DB {
    open: extern "C" fn()
}

let db = ...  // Get DB from somewhere
(db.open)()   // Parentheses around db.open are needed to disambiguate field access

但是,这确实应该包含在某种impl基于 - 的接口中,因为调用 extern 函数是不安全的操作,并且您不希望您的用户unsafe绕过所有数据库交互。

于 2013-08-08T05:36:45.837 回答
0

鉴于 DB 结构的庞大规模和复杂性,似乎没有一种“干净”的方式将整个事物暴露给 Rust。一个类似于C2HS的从 C 头文件生成 FFI 的工具会很好,但可惜我们没有。

另请注意,Rust FFI 目前无法调用 C++ 库,因此您必须改用 C API。

我根本不熟悉 DB API,但在 C 中创建一个小型支持库以实际创建 DB 结构的实例,然后公开struct __dbvia getter 和 setter 函数的公共成员似乎是合理的。

您的实现可能如下所示:

[#link_args = "-lrust_dbhelper"]
extern {
    fn create_DB() -> *c_void;
    fn free_DB(db: *c_void);
}

struct DB {
     priv db: *c_void
}

impl Drop for DB {
    fn drop(&self) {
       free_DB(self.db);
    }
}

priv struct DBAppMembers {
    pgsize: u32,
    priority: DBCachePriority
    // Additional members omitted for brevity
}

impl DB {
    pub fn new() -> DB {
        DB {
          db: create_DB()
        }
    }

    pub fn set_pgsize(&mut self, u32 pgsize) {
        unsafe {
            let x: *mut DBAppMembers = ::std::ptr::transmute(self.db);
            x.pgsize = pgsize;
        }
    }
    // Additional methods omitted for brevity
}

您可以通过将DB.db成员作为参数专门调用 C 函数来避免一些额外的工作,但这需要在不安全的上下文中工作,应该尽可能避免这种情况。否则,由导出的每个函数libdb都需要在您的 native 中有自己的包装器struct DB

于 2013-08-07T22:08:35.217 回答