unsafe fn instance_import<T>(name: *const u8) -> Option<T> {
match vkGetInstanceProcAddr(std::ptr::null_mut(), name as *const i8) {
Some(ptr) => {
return Some(std::mem::transmute(ptr));
}
None => {
return None
}
}
}
Hello, can anyone with knowledge of the black magic known as FFI help me with transmutation of function hands?
In the code below I use a generic function to get the pointer to an instance function and return an option with the original type converted to the destination.
But the compiler says that the destination type has a variable size, but the type is known and has a fixed size, 64 bits.
The strangest thing is that in the warning message it does not inform the destination type.
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
16 | return Some(std::mem::transmute(ptr));
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `unsafe extern "C" fn()` (64 bits)
= note: target type: `T` (this type does not have a fixed size)
It does tell you what the destionation type is. It inferred it to be the generic type T, i.e. whatever type the caller of instance_import decided it should be. Since the caller can choose whatever, the size is not known.
Maybe you needed to read the pointer or something? It's not entirely clear what it's supposed to be.
Both are pointers to functions, it has 64 bits, but the generic function does not recognize the destination type, even using the turbofish.
I need to load function pointers, which are returned as void(*) (void) by the loader and convert them to the correct type.
What if I called instance_import2::<std::fs::File>? Then the return type is Result<File>, but that's certainly not what you are returning. Change the return type to the actual thing you are trying to return.
When you use generics, the compiler enforces that you code is valid for all possible choices of types that satisfy the trait bounds (you have no trait bounds). If you code would not be valid for all possible types in place of T, it will not compile.
It doesn't just look at what types you call it with.
It's possible that you could convince the compiler to just ignore any problems, which would of course cause very bad things to happen if it called with a bad type T.
@rafaelcout if you still want to use generics, you need to express that you will only be using function pointers as the type T. This is done by using a trait, and implementing it for all the function pointers you want to be using:
unsafe
trait IsFunctionPointer : Sized {
/// ## Safety
///
/// The input `PTR_vkVoidFunction` must point to a valid function
/// pointer of type `Self`.
unsafe
fn transmute_from_void_function (
_: PTR_vkVoidFunction,
) -> Self;
}
impls!( _5, _4, _3, _2, _1 );
macro_rules! impls {(
$( $_0:ident $(, $_k:ident)* $(,)? )?
) => (
$( impls!( $($_k),* ); )?
unsafe
impl<Ret, $($_0 $(, $_k)*)?> IsFunctionPointer
for unsafe extern "C" fn($($_0 $(, $_k)*)?) -> Ret
{
unsafe
fn transmute_from_void_function (
ptr: PTR_vkVoidFunction,
) -> Self
{
::core::mem::transmute(ptr)
}
}
)} use impls;
And now you can write:
unsafe
fn instance_import<T> (name: *const u8) -> Option<T>
+ where
+ T : IsFunctionPointer,
{
match vkGetInstanceProcAddr(std::ptr::null_mut(), name as *const i8) {
Some(ptr) => {
- return Some(std::mem::transmute(ptr));
+ return Some(T::transmute_from_void_function(ptr));
},
None => {
return None
},
}
}