以前当代码库是在C++中时,我有C++包装器文件,它将链接到代码库,我将运行swig(支持C++ 11的版本3)来生成目标语言的接口文件(Python,JavaScript, C#等).然后当然将所有这些文件和库编译成一个共享对象,并从所需的语言中调用它.现在代码库被改为生锈.因此,对于swig工作,我有以下内容:
no_mangle和extern语法FFI并编译成staticlib.现在我swig在C文件上使用,获取目标语言的接口文件,将所有文件(第二步和第三步)和SWIG接口文件组合成一个共享对象,并从目标语言调用.
所以:
方法好吗?
我可以获得免费的功能.但是我对如何使成员函数(方法)工作感到困惑.在C++中,成员函数的第一个参数是隐式this指针.所以我可以void*将类或结构的句柄返回给C接口,将其传递给想要存储它的其他人(例如Firefox的jsctypes),然后再将reinterpret_cast它接收到具体/实际类型并调用成员函数在上面.我怎么用Rust做到这一点?
例如,为
pub struct A { id: SomeType, }
impl A {
pub fn some_funct_0(&mut self) {}
pub fn some_funct_1(&self) {}
}
impl SomeTrait for A {
fn some_trait_funct(&mut self) {}
}
Run Code Online (Sandbox Code Playgroud)
那么如何A从目标语言(Python,C等)甚至简单的C接口访问这些成员函数(应该是非托管的,在堆上我猜?)?
好吧,方法只是常规函数,正如克里斯所说,self参数与Self类型具有隐式联系。对于您的示例(稍作修改),使用C代码中的函数应该很简单:
#[repr(C)]
pub struct A { id: u32, }
#[no_mangle]
pub extern fn new_a(id: u32) -> A {
A { id: id }
}
impl A {
#[no_mangle]
pub extern fn some_funct(&self) {
println!("Called some_funct: {}", self.id);
}
}
trait SomeTrait {
extern fn some_trait_funct(&self);
}
impl SomeTrait for A {
#[no_mangle]
extern fn some_trait_funct(&self) {
println!("Called some_trait_funct: {}", self.id);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我添加extern了更改调用约定,并#[no_mangle]避免#[repr(C)]在结构上进行名称修饰的操作。如果您的代码创建Box结构的es并将它们作为原始指针传递给C,则后者是不必要的。但是,我不确定#[no_mangle]如果有多个特征实现者,如何影响特征方法-如果两者都具有#[no_mangle],那么肯定会发生某种名称冲突。
现在使用这种类型及其在C中的功能很容易:
#include <stdint.h>
struct A {
uint32_t id;
};
extern struct A new_a(uint32_t id);
extern void some_funct(const struct A *self);
extern void some_trait_funct(const struct A *self);
int main() {
struct A a = new_a(123);
some_funct(&a);
some_trait_funct(&a);
}
Run Code Online (Sandbox Code Playgroud)
该程序可以编译并运行:
% rustc --crate-type=staticlib test.rs
multirust: a new version of 'nightly' is available. run `multirust update nightly` to install it
note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: System
note: library: pthread
note: library: c
note: library: m
% gcc -o test_use test_use.c libtest.a -lSystem -lpthread -lc -lm
% ./test_use
Called some_funct: 123
Called some_trait_funct: 123
Run Code Online (Sandbox Code Playgroud)
如果接受方法&mut self:
% rustc --crate-type=staticlib test.rs
multirust: a new version of 'nightly' is available. run `multirust update nightly` to install it
note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: System
note: library: pthread
note: library: c
note: library: m
% gcc -o test_use test_use.c libtest.a -lSystem -lpthread -lc -lm
% ./test_use
Called some_funct: 123
Called some_trait_funct: 123
Run Code Online (Sandbox Code Playgroud)
您需要省略const:
#[no_mangle]
extern fn some_funct_mut(&mut self) { ... }
Run Code Online (Sandbox Code Playgroud)
如果接受方法self:
extern void some_funct_mut(struct A *self);
Run Code Online (Sandbox Code Playgroud)
您将需要按值传递结构:
#[no_mangle]
extern fn some_funct_value(self) { ... }
Run Code Online (Sandbox Code Playgroud)
尽管如果通过不透明的指针使用该结构,则调用按值将其取值的函数可能很困难,因为C必须知道该结构的确切大小。我相信,这并不是不透明指针常见的情况。
好吧,正如我在接受的答案中评论我无法使用这种方法,我最终做了类似的事情,以便其他人评论:
后端生锈代码被编译为rlib:
pub trait TestTrait {
fn trait_func(&mut self) -> i32;
}
pub struct TestStruct {
value: i32,
}
impl TestStruct {
pub fn new(value: i32) -> TestStruct {
TestStruct {
value: value,
}
}
pub fn decrement(&mut self, delta: i32) {
self.value -= delta;
}
}
impl TestTrait for TestStruct {
fn trait_func(&mut self) -> i32 {
self.value += 3;
self.value
}
}
Run Code Online (Sandbox Code Playgroud)
上面的防锈包装器链接到上面rlib并编译成staticlib(例如,.a在Linux等中):
#[no_mangle]
pub extern fn free_function_wrapper(value: i32) -> i32 {
rustlib::free_function(value)
}
#[no_mangle]
pub extern fn new_test_struct_wrapper(value: i32) -> *mut libc::c_void {
let obj = rustlib::TestStruct::new(value);
unsafe {
let raw_ptr = libc::malloc(mem::size_of::<rustlib::TestStruct>() as libc::size_t) as *mut rustlib::TestStruct;
ptr::write(&mut *raw_ptr, obj);
raw_ptr as *mut libc::c_void
}
}
#[no_mangle]
pub extern fn test_struct_decrement_wrapper(raw_ptr: *mut libc::c_void, delta: i32) {
unsafe {
mem::transmute::<*mut libc::c_void, &mut rustlib::TestStruct>(raw_ptr).decrement(delta);
}
}
#[no_mangle]
pub extern fn test_struct_trait_function_wrapper(raw_ptr: *mut libc::c_void) -> i32 {
unsafe {
mem::transmute::<*mut libc::c_void, &mut rustlib::TestStruct>(raw_ptr).trait_func()
}
}
Run Code Online (Sandbox Code Playgroud)
C-wrapper(api.h & api.c)链接到staticlib上面的内容并在需要时编译成共享对象:
extern int32_t free_function_wrapper(int32_t value);
extern void* new_test_struct_wrapper(int32_t value);
extern void test_struct_decrement_wrapper(void* ptr, int32_t delta);
extern int32_t test_struct_trait_function_wrapper(void* ptr);
int32_t free_function(int32_t value) {
return free_function_wrapper(value);
}
void* new_test_struct(int32_t value) {
return new_test_struct_wrapper(value);
}
void test_struct_decrement(void* ptr, int32_t value) {
test_struct_decrement_wrapper(ptr, value);
}
int32_t test_struct_trait_function(void* ptr) {
return test_struct_trait_function_wrapper(ptr);
}
Run Code Online (Sandbox Code Playgroud)
现在只需在目标语言上运行SWIGC文件(我只发布.c文件 - 你可以猜出.hSWIG将在哪个上运行),interface_wrap.c通过它获取生成的(默认名称)并编译这些源代码链接,staticlib获得共享对象.
例如,对于python:
swig -python interface.i
gcc -std=c99 -c -fPIC -Wall -Werror -O2 api.c interface_wrap.c -I/usr/include/python2.7
gcc -shared -o _port_sample.so api.o interface_wrap.o -L./ -lrust_wrapper
Run Code Online (Sandbox Code Playgroud)
现在只需从Python调用,整个过程就可以了:
$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import port_sample
>>> handle = port_sample.new_test_struct(36)
>>> port_sample.test_struct_decrement(handle, 12)
>>> value = port_sample.test_struct_trait_function(handle)
>>> print value
27
>>> exit()
Run Code Online (Sandbox Code Playgroud)
我希望有人发现这有用和/或可以提出改进等.我也有这个东西工作并致力于我的github回购:https://github.com/ustulation/rust-ffi/tree/master/python-swig -锈