C 我可以用Rust中的任意参数调用原始指针吗?

C 我可以用Rust中的任意参数调用原始指针吗?,c,reflection,rust,x86-64,C,Reflection,Rust,X86 64,我正试图在一个更复杂的场景中将我的Rust程序与一个用C编写的库结合起来 该库提供以下接口: 使用std::os::raw:{c_char,c_void}; 外部“C”{ pub-fn寄存器函数( 姓名:*常量c_char,签名:*常量c_char, 职能部门:*mut c_void,附件:*mut c_void, ); } 签名可以是一个字符串,将函数的参数和返回类型描述为32位或64位整数和浮点(表示法:b'i'i32,b'i'=i64,b'f'=f32,b'f'=f64)。使用与签名参数

我正试图在一个更复杂的场景中将我的Rust程序与一个用C编写的库结合起来

该库提供以下接口:

使用std::os::raw:{c_char,c_void};
外部“C”{
pub-fn寄存器函数(
姓名:*常量c_char,签名:*常量c_char,
职能部门:*mut c_void,附件:*mut c_void,
);
}
签名可以是一个字符串,将函数的参数和返回类型描述为32位或64位整数和浮点(表示法:
b'i'
i32,
b'i'
=
i64
b'f'
=
f32
b'f'
=
f64
)。使用与签名参数相对应的
u64
uint64\t
)值数组调用注册函数

我想抽象这个注册和回调过程,以便将来可以切换到另一个提供类似但不同接口的库。我的想法是创建一个注册的代理函数,而不是实际的函数。这还将提供一个自定义上下文结构

我自己的函数可以如下所示:

使用std::boxed::Box;
使用std::pin::pin;
fn返回无效(上下文:Pin){
// ...
}
fn返回_32(上下文:Pin,a:u32,b:u32)->u32{
上下文。重要的东西();
// ...
}
//浮点值很好,但是是可选的
fn return_64(a:i32,b:i64,c:f64)->f64{
// ...
}
MyAttachment
应该是上下文并提供代理函数,该函数以数组形式获取任意数量的参数:

使用std::cmp;
使用std::ffi::CString;
使用std::slice;
#[衍生(部分)]
枚举返回类型{
无效的
比特32,
位64,
}
结构MyAttachment{
real_func_ptr:*mut c_void,
签名:String,
argc:u32,
通过附件:bool,
返回类型:返回类型,
}
植入我的附件{
酒吧fn重要的东西(&self){
// ...
}
不安全的外部“C”fn函数代理(附件:*mut C\U void,argv:*mut u64){
//给定:attachment是指向MyAttachment的指针,argv是参数数组。
让this=attachment.cast::();
如果此.is_null()| | argv.is_null(){
//错误处理
返回;
}
让this=Pin::new_未选中(Box::from_raw(this));//还原
设args=slice::from_raw_parts_mut(
argv,
//如果函数应该返回值,则argv中至少有一个元素,
//因为我们需要把结果写在那里。
最大(
与此匹配。返回\u类型{
ReturnType::VOID=>0,
ReturnType::BITS32 | ReturnType::BITS64=>1,
},
此.argc作为usize,
),
);
设func_ptr=this.real_func_ptr;
//我可以从签名中获取参数类型。
//TODO强制转换为正确的指针类型。例如:
//案例返回_无效:Fn(Pin)
//外壳返回_32:Fn(引脚,u64,u64)->u32
//案例返回\u 64:Fn(u64,u64,f64)->f64
//TODO称之为:
如果this.return\u type!=ReturnType::VOID{
//args[0]=func_ptr(…args);
//或
//args[0]=func_ptr(此,…args);
}否则{
//func_ptr(…args);
}
Box::into_raw(Pin::into_inner_unchecked(this));//延迟删除此
}
}
fn main(){
//定义如下功能:
设func1=Box::pin(我的附件{
real_func_ptr:将_32返回为*mut,
签名:String::from(“(ii)i”),
argc:2,//根据签名推断
通过附件:正确,
return\u type:ReturnType::BITS32,//从签名推断
});
让name=CString::new(“return_32”).unwrap();
让signature=CString::new(func1.signature.as_str()).unwrap();
//泄漏原始指针
让func1_ptr=Box::into_raw(不安全{Pin::into_inner_unchecked(func1)});
设_func1=unsafe{Pin::new_unchecked(Box::from_raw(func1\u ptr))};//仅用于内务管理
不安全{
寄存器u函数(
name.as_ptr(),
签名。as_ptr(),
MyAttachment::函数作为*mut\u代理,
func1_ptr作为*mut_,
)
};
// ...
//这里的某个地方是我从C调用的代理
// ...
//自动清理MyAttachment结构,因为已删除这些框
}
如何用代码填充这些TODO

我在C代码中的某个地方看到了这一点,使用了通用函数指针并定义了固定数量的调用:

void(*func_ptr)();
如果(argc==0)
func_ptr();
else if(argc==1)
func_ptr(argv[0]);
else if(argc==2)
func_ptr(argv[0],argv[1]);
// ... 等等
但是有没有一种方法可以解决生锈的问题呢?(这仅适用于x86_64/amd64)

提前感谢您阅读所有这些内容并尝试提供帮助

(我添加了反射标签,因为如果生锈,这将通过反射完成)

==编辑 我见过这些相关问题,但我认为它们不适用于这里:

  • ->我的类型不是在编译时给出的
  • ->我的论点是固定大小的,不使用valist

即使是手工编写的asm,x86-64 System V调用约定也很难有效地执行此操作,因为要将一组参数加载到正确的寄存器中。我想也许你可以把它们分成前6个整数和前8个FP参数,然后你可以从一个固定的位置加载每个寄存器。对于Windows x64,这是一种不同的调用约定,不那么令人讨厌。(我认为Windows上的Rust使用Windows x64约定,以防万一)未知参数列表在C中是一个错误,出于遗留原因仍然存在,但我认为没有人建议使用它。