如何将调用包装到在Rust中使用VarArgs的FFI函数?

Cam*_*art 5 ffi variadic-functions rust

mexPrintf就像printf,接受一个varargs参数列表,但我不知道在Rust中包装它的最佳方法是什么.有一个可变参数泛型的RFC,但我们今天能做些什么呢?

在这个例子中,我想打印输入和输出的数量,但包装函数只打印垃圾.知道如何解决这个问题吗?

在此输入图像描述

#![allow(non_snake_case)]
#![allow(unused_variables)]

extern crate mex_sys;

use mex_sys::mxArray;
use std::ffi::CString;
use std::os::raw::c_int;
use std::os::raw::c_void;

type VarArgs = *mut c_void;

// attempt to wrap mex_sys::mexPrintf
fn mexPrintf(fmt: &str, args: VarArgs) {
    let cs = CString::new(fmt).unwrap();
    unsafe {
        mex_sys::mexPrintf(cs.as_ptr(), args);
    }
}

#[no_mangle]
pub extern "system" fn mexFunction(
    nlhs: c_int,
    plhs: *mut *mut mxArray,
    nrhs: c_int,
    prhs: *mut *mut mxArray,
) {
    let hw = CString::new("hello world\n").unwrap();
    unsafe {
        mex_sys::mexPrintf(hw.as_ptr());
    }

    let inout = CString::new("%d inputs and %d outputs\n").unwrap();
    unsafe {
        mex_sys::mexPrintf(inout.as_ptr(), nrhs, nlhs);
    }

    mexPrintf("hello world wrapped\n", std::ptr::null_mut());

    let n = Box::new(nrhs);
    let p = Box::into_raw(n);
    mexPrintf("inputs %d\n", p as VarArgs);

    let mut v = vec![3];
    mexPrintf("vec %d\n", v.as_mut_ptr() as VarArgs);
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 5

与流行的看法相反,可以调用 C中定义的variadic/vararg函数.这并不意味着这样做很容易,而且做坏事肯定更容易,因为编译器的类型更少检查你的工作.

这是一个打电话的例子printf.我对所有事情进行了硬编码:

extern crate libc;

fn my_thing() {
    unsafe {
        libc::printf(b"Hello, %s (%d)\0".as_ptr() as *const i8, b"world\0".as_ptr(), 42i32);
    }
}

fn main() {
    my_thing()
}
Run Code Online (Sandbox Code Playgroud)

请注意,我必须非常明确地确保我的格式字符串和参数都是正确的类型,并且字符串是NUL终止的.

通常,您将使用以下工具CString:

extern crate libc;

use std::ffi::CString;

fn my_thing(name: &str, number: i32) {
    let fmt = CString::new("Hello, %s (%d)").expect("Invalid format string");
    let name = CString::new(name).expect("Invalid name");

    unsafe {
        libc::printf(fmt.as_ptr(), name.as_ptr(), number);
    }
}

fn main() {
    my_thing("world", 42)
}
Run Code Online (Sandbox Code Playgroud)

Rust编译器测试套件还有一个调用可变参数函数的例子.


专门为一个字的警告printf式的功能:C编译器编写者意识到,人们搞砸了这种特殊类型的可变参数函数调用的所有的时间.为了帮助解决这个问题,他们编写了特殊的逻辑来解析格式字符串,并尝试根据格式字符串期望的类型检查参数类型.Rust编译器不会为您检查您的C风格格式字符串!