如何生成具有类似 `main` 接口的 FFI 函数?

How do I produce an FFI function with a `main`-like interface?

我正在尝试使用 Rust 与一个古老的平台交互,该平台期望函数以特定的 C 风格格式声明和外部化。

char *func(int argc, char *argv[])

我正在尝试在 Rust 中重现此签名。我需要能够正确解析参数(理想情况下是 Rust 字符串),然后 return 一个任意值作为字符串而不破坏签名。

到目前为止,我有以下内容:

#[no_mangle]
pub extern "C" fn my_function(argc: isize, argv: *const c_char) -> (CString) {

    let output: CString = CString::new("Test Output").expect("CString::new failed");

    return output;
}

通过接口正确 return 测试字符串,但我无法弄清楚如何将 argv 解析为可用格式,或者我的 argv 实现是否与指定格式正确兼容.我对指针不熟悉,我什至不知道从哪里开始解决这个问题。

您现有的代码存在几个主要问题:

  1. argv: *const c_char 是错误的,因为原始 C 代码是一个指针数组,而不是指向 char 的指针。正确的类型是

    argv: *const *const c_char
    
  2. 你不能 return CString 因为那不是 C 知道如何处理的类型。您需要 return *mut c_char 就像 C 函数所做的那样。为此,您使用 output.into_raw() to unwrap the string and get the underlying *mut c_char pointer. This also means that you are giving this C code a random pointer, and it can read it as a C string, but it can't free that memory, so you're either leaking that memory, or you need a second function to pass the string back to Rust to be freed later using from_raw。在这种情况下,我使用 *mut c_char 因为那是 into_raw returns.

  3. 您应该始终对 C 接口中的所有内容使用 c_ 类型,包括 argc,例如

    my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char
    

要处理 args,您需要遍历 argc 值以将每个指针包装到一个 CStr 中,然后您可以使用它而无需担心不安全代码。

总计:

#[no_mangle]
pub extern "C" fn my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char {
    
    let argv: Vec<_> = (0..argc)
        .map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
        .collect();

    // Whatever processing you need to do.

    let output = CString::new("Test Output").expect("CString::new failed");

    return output.into_raw();
}

并且可能

#[no_mangle]
pub extern "C" fn my_function_free(s: *mut c_char) {
    unsafe {
        CString::from_raw(s);
    }
}