How to bind a call to an FFI function that uses VarArgs in Rust?

mexPrintf , like printf , accepts a list of varargs arguments. I don't know how best to wrap this in Rust. There is an RFC for variable generics , but what can we do today?

In this example, I want to print the number of inputs and outputs, but the wrapped function just prints the garbage. Any idea how to fix this?

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

Update: I confused variable list of arguments with va_list . I am going to avoid both, if I can in this situation, I am just going to format the strings in Rust before passing it for interaction. Here is what worked for me in this case:

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

+5
source share
1 answer

Contrary to popular belief, you can call variables / vararg functions that were defined in C. This does not mean that it is very simple, and it is definitely easier to do something bad, because there are even fewer types for the compiler to check your work.

Here is an example call to printf . I am hardcoded about almost everything:

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

Note that I must make it very clear that my lines and formatting arguments are the correct types, and the lines have a NUL termination.

You will usually use tools like 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) } 

The Rust compiler test suite also has an example of calling a variational function .


A word of warning specifically for printf functions: C-realist compilers realized that people click this type of variational function every time. To deal with this, they coded a special logic that parses the format string and tries to check the types of arguments for the types expected in the format. The Rust compiler will not check your C format strings for you!

+4
source

Source: https://habr.com/ru/post/1259303/


All Articles