如何在Rust中包装对使用VarArgs的FFI函数的调用? [英] How to wrap a call to a FFI function that uses VarArgs in Rust?

查看:99
本文介绍了如何在Rust中包装对使用VarArgs的FFI函数的调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

解决方案

与流行的看法相反,可以调用用C定义的可变参数/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()
}

请注意,我必须非常明确地确保我的格式字符串和参数是所有正确的类型,并且字符串以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编译器测试套件还具有调用可变参数函数的示例.


专门针对类似printf的函数的警告语:C编译器编写者意识到,人们总是在不断弄乱这种特殊的可变参数函数调用 .为了解决这个问题,他们编码了特殊的逻辑,该逻辑分析格式字符串,并尝试根据格式字符串期望的类型检查参数类型. Rust编译器不会为您检查C样式格式的字符串!

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

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

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

解决方案

Contrary to popular belief, it is possible to call variadic / vararg functions that were defined in C. That doesn't mean that doing so is very easy, and it's definitely even easier to do something bad because there are even fewer types for the compiler to check your work with.

Here's an example of calling printf. I've hard-coded just about 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 have to very explicitly make sure my format string and arguments are all the right types and the strings are NUL-terminated.

Normally, you'll 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 variadic function.


A word of warning specifically for printf-like functions: C compiler-writers realized that people screw up this particular type of variadic function call all the time. To help combat that, they've encoded special logic that parses the format string and attempts to check the argument types against the types the format string expect. The Rust compiler will not check your C-style format strings for you!

这篇关于如何在Rust中包装对使用VarArgs的FFI函数的调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆