如何从其他语言访问Rust

时间:2015-05-07 06:46:27

标签: rust swig

以前当代码库是用C ++编写的时候,我有C ++包装器文件链接到代码库,我会运行swig(版本3支持C ++ 11)来生成接口文件目标语言(Python,JavaScript,C#等)。然后当然将所有这些文件和库编译成一个共享对象,并从所需的语言中调用它。现在代码库被改为生锈。因此,对于swig工作,我有以下内容:

  1. 主要生锈代码文件编译成rlib。
  2. Rust包装器文件,它调用主代码库但对no_mangle使用externFFI语法并编译为staticlib。
  3. 一个调用防锈包装器的C文件,它是它的副本。
  4. 现在我在C文件上使用swig,获取目标语言的接口文件,将所有文件(第二步和第三步)和SWIG接口文件合并到一个共享对象中并从目标调用语言。

    所以:

    1. 方法是否正常?

    2. 我可以获得免费的功能。但是我对如何使成员函数(方法)工作感到困惑。在C ++中,成员函数的第一个参数是隐式this指针。所以我可以将一个void*句柄返回给C接口的类或结构,然后将其传递给想要存储它的其他人(例如Firefox的jsctypes),然后再次接收reinterpret_cast它具体/实际类型并在其上调用成员函数。我如何使用Rust?

    3. 例如,

      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) {}
      }
      

      那么如何从目标语言(Python,C等)甚至只是简单的C接口访问A的对象(应该是非托管的,在堆上我猜?)上的这些成员函数?

2 个答案:

答案 0 :(得分:5)

好的,正如我在接受的答案中评论我无法使用这种方法,我最终做了类似这样的事情,以便其他人评论:

编译为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
    }
}

上面的防锈包装器链接到上面的rlib并编译成staticlib(例如,Linux中的.a等):

#[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()
    }
}

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);
}

现在只需在C文件上运行SWIG(我只发布了.c文件 - 您可以猜测SWIG将运行的.h目标语言,获取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

现在只需从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()

我希望有人觉得这很有用,并且/或者可以提出改进建议等等。我也让这件事工作并致力于我的github回购:https://github.com/ustulation/rust-ffi/tree/master/python-swig-rust

答案 1 :(得分:4)

嗯,方法只是常规函数,正如Chris所说,\\servername\sharename\folder\file.ext参数与self类型有隐式连接。使用C代码中的函数的示例(略微修改)应该很简单:

Self

请注意,我添加了#[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); } } 来更改调用约定,添加了extern以避免在结构上进行名称修改和#[no_mangle]。如果您的代码创建结构的#[repr(C)]并将它们作为原始指针传递给C,则后者不是必需的。但是,如果有多个特征实现者,我不确定Box如何影响特征方法 - 如果两者都有#[no_mangle],则必然存在某种名称冲突。

现在使用这种类型及其C函数很简单:

#[no_mangle]

该程序编译和工作:

#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);
}

如果接受方法% 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

&mut self

您需要省略#[no_mangle] extern fn some_funct_mut(&mut self) { ... }

const

如果接受方法extern void some_funct_mut(struct A *self);

self

您需要按值传递结构:

#[no_mangle]
extern fn some_funct_value(self) { ... }

虽然如果你通过一个不透明的指针使用该结构,调用按值取值的函数可能很困难,因为C必须知道结构的确切大小。我认为并不是说它与不透明指针有共同之处。