先说c++的情况,大的思路是变c
这个方法很多,说我习惯的。
extern c写一个wrapper.cpp把cpp里面c没有的语法给消除了。然后写一个c的wrapper.c。可以走静态和动态编译两条路。
在拿到的就是静态库的情况下,可以把静态库链接到动态库,
// g++ -c -shared -fPIC apple.cpp AppleWrapper.cpp
// ar -r libapple.a apple.o AppleWrapper.o
// gcc test.c -fPIC -shared -o test2.so -L. libapple.a -lstdc+
再说rust和c互动,这个太方便,但是有很多细节散乱在各个地方,官方文档并未说清楚(至少对于我这种非熟练的c++选手)。
extern "C" {
pub fn say_str(a: *const ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char;
}
fn use_say_str() {
unsafe {
let c_to_print = CString::new("Hello, world!").expect("CString::new failed");
let arg1 = c_to_print.as_ptr();
let res1 = b::say_str(arg1);
// from raw等于释放内存
let res2 = CString::from_raw(res1);
dbg!(res2);
}
}
extern "C" {
pub fn init_man() -> *mut man;
}
fn use_man() {
unsafe {
let a = b::init_man();
// from raw等于释放内存
let m = Box::from_raw(a);
let n = m.name;
let name = ptr_cstring(n, 6);
let age = m.age;
dbg!(age,name);
}
// 下面这两句多余,因为from raw等于释放内存
// let p = Box::into_raw(m);
// unsafe { b::free_man(p) };
}
4 说个坑。如果struct里面含有char*。如果传递struct有string,一定要把str长度传递进来。通过std::slice::from_raw_parts把指针(头地址)以及后面内容拷贝到slice,直接使用CString from_ptr的方法会发生段错误。
fn ptr_cstring(ptr: *mut i8, l: usize) ->String {
let res=
unsafe {
// 把一个字符串指针,copy到一个slice bytes返回地址,数组应该也可以用这种方法
let arr: &[u8] = std::slice::from_raw_parts(ptr as *const u8, l);
CString::new(arr).expect("c string error")
};
let a=res.into_string().unwrap();
a
}
5 再说一个查了好久才知道的,build.rs 如何包裹库文件,分为三种情况:
5.1直接使用cc编译c源码是下面的方式build_c,无坑,cc可以根据系统调用合适的编译器。
#[allow(dead_code)]
fn build_c() {
cc::Build::new()
.file("src/double.c")
.compile("libdouble.a");
}
5.2使用bindgen根据wrapp h生成rust接口文件bindings rs,打包静态库。注意这里面传输传递很特殊,通过println cargo::的方式,这个是设置环境变量,不是打印让你看着玩的。
#[allow(dead_code)]
fn set_compile_link_lib() {
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let lib = format!("{}/src/lib/", root);
println!("cargo:warning=MESSAGE");
println!("cargo:rustc-link-search=all={}", lib);
println!("cargo:rustc-link-lib=static=wq");
// println!("cargo:rustc-link-lib=dylib=wq");
}
如果打包的是动态库,需要// export LD_LIBRARY_PATH=你的库的路径:$LD_LIBRARY_PATH
// echo $LD_LIBRARY_PATH
5.3 运行如果是动态链接加载libload so不需要buildrs
extern crate libloading as lib;
pub fn load_dynamic() -> lib::Result {
let file="/mnt/myrust/rust-ffi-examples-master/rust-call-c/src/lib/libtest.so";
let lib = lib::Library::new(file)?;
unsafe {
let func: lib::Symbol u32> = lib.get(b"say_int")?;
Ok(func(18))
}
}
#[test]
pub fn lib_load_so() {
let a = load_dynamic().unwrap();
dbg!(a);
}