I'm using the libjit library to jit compile functions. After compiling a function to a closure it returns a *mut ::std::os::raw::c_void
. This void pointer is a actually a function I could call like so:
For example
let void_ptr = jit_function_to_closure(self.function);
let func_ptr: fn(i32, i32,i32) -> i32 = std::mem::transmute(void_ptr);
func_ptr(1,2,3);
This works fine but requires I know the signature of every function I want to compile.
I want to wrap this snippet in a function that accepts a function pointer type eg. fn(i32, i32,i32) -> i32
as a generic arg so I can return a function pointer of any type specified by the caller.
Something like:
// My library
pub fn to_closure<T>(&self) -> T {
unsafe {
let void_ptr = jit_function_to_closure(self.function);
std::mem::transmute(void_ptr)
}
}
// Caller
let func = ....
let callable = fn(f64, f64) -> i32 = func.to_closure();
This does not compile
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> src/function.rs:57:13
|
57 | std::mem::transmute(void_ptr)
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `*mut c_void` (64 bits)
= note: target type: `T` (this type does not have a fixed size)
Is this possible? Is there a generic bound for a function pointer of arbitrary signature?
CodePudding user response:
No, there is not trait like that. You have two options:
- Leave it as-is, but since
transmute()
cannot be used for unknown-sized types, use the usual workaround -transmute_copy()
:unsafe { std::mem::transmute_copy(&void_ptr) }
. - This is what I would prefer: create a trait to encapsulate the notion of "being a function pointer". Since Rust has no variadics, you'll have to use macros and it will not cover all functions, but it should cover all usable cases. Here I present a macro that only generates impls for the
extern "C"
calling convention, but adapting it to using other calling conventions should not be difficult:
trait FnPtr {
unsafe fn from_void_ptr(ptr: *mut c_void) -> Self;
}
macro_rules! impl_fnptr {
// Recursion exit condition
() => {};
($first:ident $($rest:ident)*) => {
impl<$first, $($rest,)* Ret> FnPtr for extern "C" fn($first, $($rest,)*) -> Ret {
unsafe fn from_void_ptr(ptr: *mut c_void) -> Self {
std::mem::transmute::<*mut c_void, Self>(ptr)
}
}
// With variadic args
impl<$first, $($rest,)* Ret> FnPtr for extern "C" fn($first, $($rest,)* ...) -> Ret {
unsafe fn from_void_ptr(ptr: *mut c_void) -> Self {
std::mem::transmute::<*mut c_void, Self>(ptr)
}
}
// Recurse
impl_fnptr!($($rest)*);
};
}
impl<Ret> FnPtr for extern "C" fn() -> Ret {
unsafe fn from_void_ptr(ptr: *mut c_void) -> Self {
std::mem::transmute::<*mut c_void, Self>(ptr)
}
}
// Impl up to 15 parameters.
impl_fnptr!(A B C D E F G H I J K L M N O);
Note, however, that the to_closure()
function needs to be marked unsafe
to be sound, since you can use the wrong signature etc..