|
2 | 2 |
|
3 | 3 | [](https://github.com/OpenByteDev/fn-ptr/actions/workflows/ci.yml) [](https://crates.io/crates/fn-ptr) [](https://docs.rs/fn-ptr) [](https://deps.rs/repo/github/openbytedev/fn-ptr) [](https://github.com/OpenByteDev/fn-ptr/blob/master/LICENSE) |
4 | 4 |
|
5 | | -This is a small utility crate that provides the [`FnPtr`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.FnPtr.html) trait, which is implemented for all function pointer types: |
| 5 | +This is a utility crate for **introspecting** and **rewriting** function pointer types at compile time. |
| 6 | + |
| 7 | +It implements [`FnPtr`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.FnPtr.html) for all function-pointer types: |
6 | 8 | - `fn(T) -> U` |
7 | 9 | - `unsafe fn(T) -> U` |
8 | | -- `extern "C" fn(T)` |
| 10 | +- `extern "C-unwind" fn(T)` |
9 | 11 | - `unsafe extern "sysv64" fn() -> i32` |
10 | 12 |
|
11 | | -The trait provides associated types and constants to introspect function pointer types at compile time. |
12 | | - |
13 | | -## Features |
14 | | - |
15 | | -### 1. Function Pointer Metadata |
| 13 | +## Function pointer metadata |
16 | 14 |
|
17 | | -Every function pointer automatically implements [`FnPtr`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.FnPtr.html) as well as a bunch of other related traits. With these you can inspect the type of function pointers at compile time: |
| 15 | +[`FnPtr`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.FnPtr.html) exposes metadata as associated types/consts. |
18 | 16 |
|
19 | 17 | ```rust |
20 | | -use fn_ptr::{FnPtr, Abi}; |
| 18 | +use fn_ptr::{FnPtr, AbiValue}; |
21 | 19 |
|
22 | 20 | type F = extern "C" fn(i32, i32) -> i32; |
23 | | - |
24 | 21 | assert_eq!(<F as FnPtr>::ARITY, 2); |
25 | 22 | assert_eq!(<F as FnPtr>::IS_SAFE, true); |
26 | 23 | assert_eq!(<F as FnPtr>::IS_EXTERN, true); |
27 | | -assert_eq!(<F as FnPtr>::abi, Abi::C { unwind: false }); |
| 24 | +assert_eq!(<F as FnPtr>::ABI, AbiValue::C { unwind: false }); |
28 | 25 | ``` |
29 | 26 |
|
30 | | -There are also some const helper functons to do so ergonomically. |
| 27 | +Ergonomic const functions are provided as well: |
31 | 28 |
|
32 | 29 | ```rust |
33 | | -const A: usize = fn_ptr::arity::<F>(); // 2 |
34 | | -const SAFE: bool = fn_ptr::is_safe::<F>(); // true |
35 | | -const EXT: bool = fn_ptr::is_extern::<F>(); // true |
36 | | -const abi: Abi = fn_ptr::abi::<F>(); // Abi::C |
| 30 | +const A: usize = fn_ptr::arity::<F>(); |
| 31 | +const SAFE: bool = fn_ptr::is_safe::<F>(); |
| 32 | +const EXT: bool = fn_ptr::is_extern::<F>(); |
| 33 | +const abi: AbiValue = fn_ptr::abi::<F>(); |
37 | 34 | ``` |
38 | 35 |
|
39 | | -### 2. Toggle Function Pointer Safety |
| 36 | +## Rewriting function-pointer types |
40 | 37 |
|
41 | | -You can toggle the safety of a function pointer at the type level: |
| 38 | +The crate provides type-level rewriting via traits: |
42 | 39 |
|
43 | | -```rust |
44 | | -use fn_ptr::{make_safe, make_unsafe}; |
| 40 | +- **abi:** [`WithAbi`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithAbi.html) / [`with_abi!`](https://docs.rs/fn-ptr/latest/fn_ptr/macro.with_abi.html) |
| 41 | +- **Safety:** [`WithSafety`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithSafety.html) / [`with_safety!`](https://docs.rs/fn-ptr/latest/fn_ptr/macro.with_safety.html) ([`make_safe!`](https://docs.rs/fn-ptr/latest/fn_ptr/macro.make_unsafe.html), [`make_unsafe!`](https://docs.rs/fn-ptr/latest/fn_ptr/macro.make_unsafe.html)) |
| 42 | +- **Output:** [`WithOutput`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithOutput.html) / [`with_output!`](https://docs.rs/fn-ptr/latest/fn_ptr/macro.with_output.html) |
| 43 | +- **Args:** [`WithArgs`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithArgs.html) / [`with_args!`](https://docs.rs/fn-ptr/latest/fn_ptr/macro.with_args.html) |
45 | 44 |
|
46 | | -type U = unsafe extern "C" fn(i32); |
47 | | -type S = make_safe!(U); // extern "C" fn(i32) |
| 45 | +### Type-level transformations |
48 | 46 |
|
49 | | -type S2 = extern "C" fn(i32); |
50 | | -type U2 = make_unsafe!(S2); // unsafe extern "C" fn(i32) |
51 | | -``` |
52 | | - |
53 | | -Or at the instance level: |
| 47 | +The macros compute a new function-pointer **type** while preserving everything else. |
54 | 48 |
|
55 | 49 | ```rust |
56 | | -let safe_add: fn(i32, i32) -> i32 = |a, b| {a + b}; |
57 | | -let unsafe_add: unsafe fn(i32, i32) -> i32 = safe_add.as_unsafe(); |
58 | | -let safe_add2: fn(i32, i32) -> i32 = unsafe { unsafe_add.as_safe() }; |
59 | | -``` |
60 | | - |
61 | | -### 3. Changing ABIs |
62 | | - |
63 | | -You can also change the abi of a function pointer at the type level: |
64 | | - |
65 | | -```rust |
66 | | -use fn_ptr::{with_abi, Abi}; |
| 50 | +use fn_ptr::{with_abi, with_safety, with_output, with_args}; |
67 | 51 |
|
68 | 52 | type F = extern "C" fn(i32) -> i32; |
69 | 53 |
|
70 | | -type G = with_abi!("sysv64", F); |
71 | | -type H = with_abi!("C", extern "system" fn()); |
| 54 | +type F1 = with_abi!("sysv64", F); // extern "sysv64" fn(i32) -> i32 |
| 55 | +type F2 = with_safety!(unsafe, F); // unsafe extern "C" fn(i32) -> i32 |
| 56 | +type F3 = with_output!(u64, F); // extern "C" fn(i32) -> u64 |
| 57 | +type F4 = with_args!((u8, u16), F); // extern "C" fn(u8, u16) -> i32 |
72 | 58 | ``` |
73 | 59 |
|
74 | | -Or at the instance level: |
| 60 | +### Value-level casts |
| 61 | + |
| 62 | +The same transformations exist at the value level via methods on [`FnPtr`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.FnPtr.html). |
| 63 | +These casts are only [`transmuting`](https://doc.rust-lang.org/std/mem/fn.transmute.html) and do **not** the actual underlying function. |
75 | 64 |
|
76 | 65 | ```rust |
77 | 66 | use fn_ptr::{FnPtr, abi}; |
78 | | -let rust_add: fn(i32, i32) -> i32 = |a, b| {a + b}; |
79 | | -// Safety: not actually safe! |
80 | | -let c_add: extern "C" fn(i32, i32) -> i32 = unsafe { rust_add.with_abi::<abi!("C")>() }; |
81 | | -``` |
82 | 67 |
|
83 | | -Note that this does not change the underlying abi and should be used with caution. |
| 68 | +let f: fn(i32, i32) -> i32 = |a, b| a + b; |
84 | 69 |
|
85 | | -## How It Works |
| 70 | +// safety |
| 71 | +let u: unsafe fn(i32, i32) -> i32 = f.as_unsafe(); |
| 72 | +let f2: fn(i32, i32) -> i32 = unsafe { u.as_safe() }; |
86 | 73 |
|
87 | | -To implement the traits for all function pointer types, there is a large [macro](https://github.com/OpenByteDev/fn-ptr/blob/master/src/impl.rs). |
88 | | -For the conversion macros the crate relies on two traits: [`WithAbi`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithAbi.html) and [`WithSafety`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithSafety.html) that can also be used directly: |
| 74 | +// abi |
| 75 | +let c: extern "C" fn(i32, i32) -> i32 = unsafe { f.with_abi::<abi!("C")>() }; |
89 | 76 |
|
90 | | -```rust |
91 | | -use fn_ptr::{FnPtr, WithAbi, WithSafety, marker::{SysV64, Unsafe}}; |
| 77 | +// output |
| 78 | +let out: fn(i32, i32) -> u64 = unsafe { f.with_output::<u64>() }; |
92 | 79 |
|
93 | | -type F = extern "C" fn(i32); |
94 | | -type G = <F as WithAbi<SysV64>>::F; |
95 | | -type U = <F as WithSafety<Unsafe>::F; |
| 80 | +// args |
| 81 | +let args: fn(u8, u16) -> i32 = unsafe { f.with_args::<(u8, u16)>() }; |
| 82 | + |
| 83 | +# assert_eq!(f.addr(), f2.addr()); |
| 84 | +# assert_eq!(f.addr(), c.addr()); |
| 85 | +# assert_eq!(f.addr(), out.addr()); |
| 86 | +# assert_eq!(f.addr(), args.addr()); |
96 | 87 | ``` |
| 88 | +## How it works |
| 89 | + |
| 90 | +Implementations are generated by a large [macro]((https://github.com/OpenByteDev/fn-ptr/blob/master/src/impl.rs)). The rewrite macros are thin wrappers |
| 91 | +over the traits [`WithAbi`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithAbi.html), [`WithSafety`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithSafety.html), [`WithOutput`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithOutput.html), [`WithArgs`](https://docs.rs/fn-ptr/latest/fn_ptr/trait.WithArgs.html) (and the corresponding `*Impl` helper traits). |
97 | 92 |
|
98 | 93 | ## License |
99 | 94 |
|
|
0 commit comments