gpt4 book ai didi

c - 如何为包含可空函数指针的 FFI 创建结构?

转载 作者:行者123 更新时间:2023-11-29 07:59:21 25 4
gpt4 key购买 nike

我有一个加载共享库插件的现有 C 程序。主 C 程序通过包含整数、字符串、函数指针等的 C 结构与这些插件交互。如何从 Rust 创建这样的插件?

请注意,(真正的)C 程序不能更改,API 也不能更改,那些是固定的、现有的东西,所以这不是关于“如何最好地支持 Rust 中的插件”的问题,而是 Rust 如何能够生成与现有 C 程序互操作的 *.so 文件。

这是一个 C 程序 + C 插件的简化示例:

/* gcc -g -Wall test.c -o test -ldl
./test ./test-api.so
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <dlfcn.h>

struct api {
uint64_t i64;
int i;
const char *name; /* can be NULL */
void (*load) (void); /* must not be NULL */
void (*hello) (const char *str); /* can be NULL */
};

int
main (int argc, char *argv[])
{
void *dl = dlopen (argv[1], RTLD_NOW);
if (!dl) { fprintf (stderr, "%s: %s\n", argv[1], dlerror ()); exit (1); }
struct api *(*get_api) (void) = dlsym (dl, "get_api");
printf ("calling get_api ...\n");
struct api *api = get_api ();
printf ("api->i64 = %" PRIi64 "\n", api->i64);
printf ("api->i = %d\n", api->i);
if (api->name)
printf ("api->name = %s\n", api->name);
printf ("calling api->load ...\n");
api->load ();
if (api->hello) {
printf ("calling api->hello ...\n");
api->hello ("world");
}
printf ("exiting\n");
exit (0);
}
/* gcc -g -shared -fPIC -Wall test-api.c -o test-api.so */

#include <stdio.h>
#include <stdint.h>

static void
load (void)
{
printf ("this is the load function in the plugin\n");
}

static void
hello (const char *str)
{
printf ("hello %s\n", str);
}

static struct api {
uint64_t i64;
int i;
const char *name;
void (*load) (void);
void (*hello) (const char *str);
} api = {
1042,
42,
"this is the plugin",
load,
hello,
};

struct api *
get_api (void)
{
return &api;
}

这是我用 Rust 编写的尝试获取插件的内容,但它无法编译:

extern crate libc;

use libc::*;
use std::ffi::*;
use std::ptr;
use std::os::raw::c_int;

#[repr(C)]
pub struct api {
i64: uint64_t,
i: c_int,

name: *const c_char,

load: extern fn (),
hello: extern fn (), // XXX
}

extern fn hello_load () {
println! ("hello this is the load method");
}

#[no_mangle]
pub extern fn get_api () -> *const api {
println! ("hello from the plugin");

let api = Box::new (api {
i64: 4201,
i: 24,
name: CString::new("hello").unwrap().into_raw(), // XXX memory leak?
load: hello_load,
hello: std::ptr::null_mut,
});

return Box::into_raw(api); // XXX memory leak?
}

这是使用 Cargo.toml 编译的,包含:

[package]
name = "embed"
version = "0.1.0"

[dependencies]
libc = "0.2"

[lib]
name = "embed"
crate-type = ["cdylib"]

错误是:

error[E0308]: mismatched types
--> src/lib.rs:32:16
|
32 | hello: std::ptr::null_mut,
| ^^^^^^^^^^^^^^^^^^ expected "C" fn, found "Rust" fn
|
= note: expected type `extern "C" fn()`
found type `fn() -> *mut _ {std::ptr::null_mut::<_>}`

error: aborting due to previous error

我没有尝试加载模块,但是当我之前用真实程序尝试加载时,字段都是错误的,表明更基本的东西是错误的。

最佳答案

tl;dr 使用 Option表示可空函数指针和 None为空。

错误信息令人困惑,首先,因为std::ptr::null_mut不是指针;它是一个返回 指针的通用函数,而您还没有调用它。所以 Rust 看到您传递了一个具有错误签名和调用约定的函数,并对此进行了提示。

但是一旦你解决了这个问题,你就会得到这个错误:

error[E0308]: mismatched types
--> src/lib.rs:29:16
|
29 | hello: std::ptr::null_mut(),
| ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found *-ptr
|
= note: expected type `extern "C" fn()`
found type `*mut _`

函数指针和对象指针不兼容(在C中也是如此),所以你不能在它们之间进行转换。 null_mut返回一个对象指针,所以你需要找到另一种方法来创建一个空函数指针。

函数指针(fn(...) -> _ 类型的值)还有另一个有趣的属性:与原始指针(*const _*mut _)不同,它们不能为空。你不需要 unsafe block 通过指针调用函数,因此创建空函数指针是不安全的,就像创建空引用一样。

如何使某些内容可为空?将其包裹在 Option 中:

#[repr(C)]
pub struct api {
// ...
load: Option<extern fn ()>,
hello: Option<extern fn ()>, // assuming hello can also be null
}

并用 Some(function) 填充它或 None :

let api = Box::new (api {
// ...
load: Some(hello_load),
hello: None,
});

使用 enum 通常不是一个好主意s,包括 Option , 在 repr(C)结构,因为 C 没有 enum等价,所以你不知道你会在另一边得到什么。但在 Option<T> 的情况下其中 T是不可为空的东西,None表示为空值,所以应该没问题。

Option的使用表示 FFI 的可空函数指针记录在 Unsafe Code Guidelines 中:

null values are not supported by the Rust function pointer types -- just like references, the expectation is that you use Option to create nullable pointers. Option<fn(Args...) -> Ret> will have the exact same ABI as fn(Args...) -> Ret, but additionally allows null pointer values.

关于c - 如何为包含可空函数指针的 FFI 创建结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54572985/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com