Skip to content

A utility crate for introspecting and rewriting function pointer types at compile time

License

Notifications You must be signed in to change notification settings

OpenByteDev/fn-ptr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fn-ptr

CI crates.io Documentation dependency status MIT

This is a utility crate for introspecting and rewriting function pointer types at compile time.

It implements FnPtr for all function-pointer types:

  • fn(T) -> U
  • unsafe fn(T) -> U
  • extern "C-unwind" fn(T)
  • unsafe extern "sysv64" fn() -> i32

Function pointer metadata

FnPtr exposes metadata as associated types/consts.

use fn_ptr::{FnPtr, AbiValue};

type F = extern "C" fn(i32, i32) -> i32;
assert_eq!(<F as FnPtr>::ARITY, 2);
assert_eq!(<F as FnPtr>::IS_SAFE, true);
assert_eq!(<F as FnPtr>::IS_EXTERN, true);
assert_eq!(<F as FnPtr>::ABI, AbiValue::C { unwind: false });

Ergonomic const functions are provided as well:

const A: usize = fn_ptr::arity::<F>();
const SAFE: bool = fn_ptr::is_safe::<F>();
const EXT: bool = fn_ptr::is_extern::<F>();
const abi: AbiValue = fn_ptr::abi::<F>();

Rewriting function-pointer types

The crate provides type-level rewriting via traits:

Type-level transformations

The macros compute a new function-pointer type while preserving everything else.

use fn_ptr::{with_abi, with_safety, with_output, with_args};

type F = extern "C" fn(i32) -> i32;

type F1 = with_abi!("sysv64", F);   // extern "sysv64" fn(i32) -> i32
type F2 = with_safety!(unsafe, F);  // unsafe extern "C" fn(i32) -> i32
type F3 = with_output!(u64, F);     // extern "C" fn(i32) -> u64
type F4 = with_args!((u8, u16), F); // extern "C" fn(u8, u16) -> i32

Value-level casts

The same transformations exist at the value level via methods on FnPtr. These casts are only transmuting and do not the actual underlying function.

use fn_ptr::{FnPtr, abi};

let f: fn(i32, i32) -> i32 = |a, b| a + b;

// safety
let u: unsafe fn(i32, i32) -> i32 = f.as_unsafe();
let f2: fn(i32, i32) -> i32 = unsafe { u.as_safe() };

// abi
let c: extern "C" fn(i32, i32) -> i32 = unsafe { f.with_abi::<abi!("C")>() };

// output
let out: fn(i32, i32) -> u64 = unsafe { f.with_output::<u64>() };

// args
let args: fn(u8, u16) -> i32 = unsafe { f.with_args::<(u8, u16)>() };

# assert_eq!(f.addr(), f2.addr());
# assert_eq!(f.addr(), c.addr());
# assert_eq!(f.addr(), out.addr());
# assert_eq!(f.addr(), args.addr());

How it works

Implementations are generated by a large macro. The rewrite macros are thin wrappers over the traits WithAbi, WithSafety, WithOutput, WithArgs (and the corresponding *Impl helper traits).

License

Licensed under the MIT license, see LICENSE for details.

About

A utility crate for introspecting and rewriting function pointer types at compile time

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages