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

时间:2016-11-04 22:05:33

标签: rust variadic-functions ffi

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

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

enter image description here

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

2 个答案:

答案 0 :(得分:5)

与流行的看法相反,有可能调用在C中定义的变量/变量函数。这并不意味着这样做很容易,而且它很容易绝对更容易做坏事,因为编译器检查你的工作的类型甚至更少。

以下是调用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()
}

请注意,我必须非常明确地确保我的格式字符串和参数都是正确的类型,并且字符串是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)
}

Rust编译器测试套件也有an example of calling a variadic function

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

答案 1 :(得分:0)

我混淆了#34变量参数列表"使用va_list。如果可以的话,我会避免这两种情况,在这种情况下,我会在将Rust传递给interop之前在Rust中进行字符串格式化。在这种情况下,这对我有用:

#![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;

// attempt to wrap mex_sys::mexPrintf
fn mexPrintf(text: &str) {
    let cs = CString::new(text).expect("Invalid text");
    unsafe {
        mex_sys::mexPrintf(cs.as_ptr());
    }
}

#[no_mangle]
pub extern "C" fn mexFunction(
    nlhs: c_int,
    plhs: *mut *mut mxArray,
    nrhs: c_int,
    prhs: *mut *mut mxArray,
) {
    mexPrintf(&format!("{} inputs and {} outputs\n", nrhs, nlhs));
}

enter image description here