@wusyong, 吳昱緯
假設我們真的很想用 printf
#include "hello.h"
#include <stdio.h>
uint32_t printf_wrapper(const char *str)
{
printf("%s\n", str);
return 0;
}
#include <stdint.h>
uint32_t printf_wrapper(const char *str);
unsafe
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
#include <stdint.h>
uint32_t printf_wrapper(const char *str);
use std::os::raw::{c_uint, c_char};
extern "C" {
printf_wrapper(strr: const *c_char) -> c_uint;
}
std::os::raw
& std::ffi
use std::os::raw::{c_uint, c_char};
use std::ffi::CStr;
extern "C" { fn printf_wrapper(strr: const *c_char) -> c_uint; }
fn main() {
unsafe {
let my_strr = "Hello World!";
let _: u32 = printf_wrapper(CStr::from(my_strr).unwrap());
}
}
#[repr(C)]
directs the compiler to use the platform-layout.
#[repr(C)]
pub struct myapp_t {
id: u32,
other_type: Box<myother_t>,
initialised: bool,
}
#[repr(C)]
pub enum FakeBool {
No = 0,
OhYea = 1
}
When not knowing (or caring) about internal layout, opaque structs can be used.
#[repr(C)]
pub struct myopaque_t { _priv: [u8; 0] }
extern "C" {
pub fn register_callback(
state: *mut c_void,
name: *const c_char,
cb: extern "C" fn(state: *mut c_void) -> *const c_uint,
);
}
build.rs
file responsible for linking code[build-dependencies]
cmake = "0.1"
use cmake::Config;
fn main() {
let mut dst = Config::new("mimalloc")
.define("MI_OVERRIDE", "OFF")
.define("MI_SECURE", "OFF")
.build_target("mimalloc-static")
.build();
dst.push("./build");
let out_name = "mimalloc";
println!("cargo:rustc-link-search=native={}", dst.display());
println!("cargo:rustc-link-lib=static={}", out_name);
}
Binding mimalloc to rust!
常用的函式庫通常都有人弄好了
Instead of write all extern "C"
block yourelf, generate them!
// Continue in build.rs ...
let bindings = bindgen::Builder::default()
.header("mimalloc/include/mimalloc.h")
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
// In src/lib.rs
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
pub struct MiMalloc;
unsafe impl GlobalAlloc for MiMalloc {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if Self::is_aligned(&layout) {
mi_malloc(layout.size()) as *mut u8
} else {
mi_malloc_aligned(layout.size(), layout.align()) as *mut u8
}
}
// ...
}
Implement Drop
trait for your instance in most cases
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
mi_free(ptr as *mut _);
}
Use platform-ABI in reverse – emit a native library
#[repr(C)]
structures/ enums.catch_unwind
use std::panic::catch_unwind;
#[no_mangle]
pub extern fn hello_rust() -> *const u8 {
"Hello, world!\0".as_ptr()
}
#[no_mangle]
pub extern fn oh_no() -> i32 {
let result = catch_unwind(|| {
panic!("Oops!");
});
match result {
Ok(_) => 0,
Err(_) => 1,
}
}
Building dynamic or static libraries
[lib]
name = "connector"
crate-type = ["cdylib"]
C code needs .h files to include – define Rust functions there.