我有一个现有的C++程序,它使用伯克利DB作为存储后端。我想用Rust重写它。有没有办法在Rust中编写一个外部函数接口来使用Berkeley DB?我已经找到了教程Rust Foreign Function Interface,但是对于BDB中使用的复杂的C结构来说,它似乎太简单了;例如,要打开一个数据库,我需要声明一个DB结构并调用DB->open()。但我不知道如何使用教程中显示的示例来实现这一点。
有人能帮上忙吗?
发布于 2013-08-08 13:36:46
嗯,查看一下BDB的C API,我发现它是由带有元素的C结构组成的-指向函数的指针。这在教程中没有解释(这很奇怪),但是Rust目前支持pointers to foreign functions。Rust reference manual中也提到了这一点。
您可以大致基于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-based接口中,因为调用外部函数是不安全的操作,并且您不希望您的用户在所有数据库交互中都使用unsafe。
发布于 2013-08-08 06:08:35
考虑到DB结构的绝对大小和复杂性,似乎没有一种“干净”的方式将整个东西暴露给Rust。一个类似于C2HS的工具可以从C标头生成FFI会很好,但可惜我们还没有。
另请注意,Rust FFI目前不能调用C++库,因此您必须使用C API。
我对DB一点也不熟悉,但是用C语言创建一个小的支持库来实际创建DB结构的实例,然后通过getter和setter函数公开struct __db的公共成员似乎是可行的。
您的实现可能如下所示:
[#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函数来节省一些额外的工作,但这需要在不安全的上下文中工作,这可能应该尽可能避免。否则,在本机struct DB中,libdb导出的每个函数都需要有自己的包装器。
https://stackoverflow.com/questions/18094976
复制相似问题