c - 我可以在 Rust 中调用带有任意参数的原始指针吗?
问题描述
我正在尝试在更复杂的场景中将我的 Rust 程序与用 C 编写的库结合起来。
该库提供了这个接口:
use std::os::raw::{c_char, c_void};
extern "C" {
pub fn register_function(
name: *const c_char, signature: *const c_char,
func_ptr: *mut c_void, attachment: *mut c_void,
);
}
签名可以是一个字符串,将函数的参数和返回类型描述为 32 或 64 位整数和浮点数(表示形式:b'i'
= i32
、b'I'
= i64
、b'f'
= f32
、b'F'
= f64
)。u64
注册的函数被调用一个( ) 值的数组,uint64_t
这些值对应于签名中的参数。
我想把这个注册和回调过程抽象出来,以便将来可以切换到另一个提供类似但不同接口的库。我的想法是创建一个注册的代理函数而不是实际函数。这也将提供一个自定义上下文结构。
我自己的函数可能如下所示:
use std::boxed::Box;
use std::pin::Pin;
fn return_void(context: Pin<Box<MyAttachment>>) {
// ...
}
fn return_32(context: Pin<Box<MyAttachment>>, a: u32, b: u32) -> u32 {
context.important_stuff();
// ...
}
// floating point values would be nice, but are optional
fn return_64(a: i32, b: i64, c: f64) -> f64 {
// ...
}
MyAttachment
应该是上下文并提供将任意数量的参数作为数组获取的代理函数:
use std::cmp;
use std::ffi::CString;
use std::slice;
#[derive(PartialEq)]
enum ReturnType {
VOID,
BITS32,
BITS64,
}
struct MyAttachment {
real_func_ptr: *mut c_void,
signature: String,
argc: u32,
pass_attachment: bool,
return_type: ReturnType,
}
impl MyAttachment {
pub fn important_stuff(&self) {
// ...
}
unsafe extern "C" fn function_proxy(attachment: *mut c_void, argv: *mut u64) {
// Given: attachment is the pointer to MyAttachment and argv is the array of arguments.
let this = attachment.cast::<Self>();
if this.is_null() || argv.is_null() {
// error handling
return;
}
let this = Pin::new_unchecked(Box::from_raw(this)); // restore
let args = slice::from_raw_parts_mut(
argv,
// There is at least one element in argv if the function is supposed to return a value,
// because we need to write our result there.
cmp::max(
match this.return_type {
ReturnType::VOID => 0,
ReturnType::BITS32 | ReturnType::BITS64 => 1,
},
this.argc as usize,
),
);
let func_ptr = this.real_func_ptr;
// I can get the argument types from the signature.
// TODO cast to correct pointer type. For example:
// case return_void: Fn(Pin<Box<MyAttachment>>)
// case return_32: Fn(Pin<Box<MyAttachment>>, u64, u64) -> u32
// case return_64: Fn(u64, u64, f64) -> f64
// TODO call it:
if this.return_type != ReturnType::VOID {
//args[0] = func_ptr(...args);
// or
//args[0] = func_ptr(this, ...args);
} else {
//func_ptr(...args);
}
Box::into_raw(Pin::into_inner_unchecked(this)); // delay dropping of this
}
}
fn main() {
// defining functions like:
let func1 = Box::pin(MyAttachment {
real_func_ptr: return_32 as *mut _,
signature: String::from("(ii)i"),
argc: 2, // inferred from signature
pass_attachment: true,
return_type: ReturnType::BITS32, // inferred from signature
});
let name = CString::new("return_32").unwrap();
let signature = CString::new(func1.signature.as_str()).unwrap();
// leak the raw pointer
let func1_ptr = Box::into_raw(unsafe { Pin::into_inner_unchecked(func1) });
let _func1 = unsafe { Pin::new_unchecked(Box::from_raw(func1_ptr)) }; // just for housekeeping
unsafe {
register_function(
name.as_ptr(),
signature.as_ptr(),
MyAttachment::function_proxy as *mut _,
func1_ptr as *mut _,
)
};
// ...
// somewhere here is my proxy called from C
// ...
// automatic cleanup of MyAttachment structs, because the Boxes are dropped
}
如何用代码填充这些 TODO?
通过使用通用函数指针并定义固定数量的调用,我在某处的 C 代码中看到了这一点:
void (*func_ptr)();
if (argc == 0)
func_ptr();
else if (argc == 1)
func_ptr(argv[0]);
else if (argc == 2)
func_ptr(argv[0], argv[1]);
// ... and so on
但是在 Rust 中有解决方案吗?(这只需要为 x86_64/amd64 工作)
提前感谢您阅读所有这些并尝试提供帮助。
(我添加了反射标签,因为如果 Rust 有的话,这将通过反射来完成)
====编辑 我已经看到了这些相关的问题,但我认为它们不适用于这里:
- 从 Rust 调用原始地址-> 我的类型在编译时没有给出
- 如何将切片的每个元素作为单独的参数传递给可变参数 C 函数?-> 我的论点有些固定大小,不使用 valist
解决方案
推荐阅读
- iup - IUP 图形示例问题
- java - Spring boot:无法将 Int 序列化为 Float
- spring - 无法在本地计算机上实例化 c3p0.ComboPooledDataSource
- sql - 你能在不使用任何'with'子句的情况下编写单个查询吗
- python - Python:在二叉搜索树中寻找共同祖先的递归
- java - 如何将数组列表从内部类传递给另一个已经启动的类?
- html - 图像未在容器中缩放
- tizen - bt_adapter_le_start_scan() 在 tizen 可穿戴本机应用程序中也扫描 BLE 设备以外的设备
- ruby-on-rails - Rails 使用时间对象查询,不返回特定于日期的对象
- javascript - 如何在没有 ID 或类的情况下访问 html 元素标签?