Skip to content

Commit 31f5d34

Browse files
committed
Split up lib.rs
1 parent 53036d1 commit 31f5d34

5 files changed

Lines changed: 383 additions & 376 deletions

File tree

src/abi.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,36 @@ pub(crate) type AbiKey = Abi;
173173
#[cfg(not(nightly_build))]
174174
pub(crate) type AbiKey = u8;
175175

176-
#[must_use]
177176
/// Returns the value used to designate the given ABI in const generics.
178177
/// For stable or beta builds this returns an [`u8`], while on nightly the [`Abi`] instance is returned.
178+
#[must_use]
179179
pub const fn key(abi: Abi) -> AbiKey {
180180
abi as _
181181
}
182+
183+
/// Converts an ABI string like "C" into the corresponding value for use in const generics.
184+
/// This is most useful for stable rust since there [`u8`]s are used.
185+
///
186+
/// # Example
187+
///
188+
/// ```rust
189+
/// # use fn_ptr::make_non_extern;
190+
/// type F = extern "C" fn(i32) -> i32;
191+
/// type R = make_non_extern!(F);
192+
/// // `R` is `fn(i32) -> i32`
193+
/// ```
194+
///
195+
/// Equivalent to:
196+
/// ```rust
197+
/// # use fn_ptr::{Abi, with_abi};
198+
/// # type F = extern "C" fn(i32) -> i32;
199+
/// # type G =
200+
/// with_abi!(Abi::Rust, F)
201+
/// # ;
202+
/// ```
203+
#[macro_export]
204+
macro_rules! abi {
205+
( $abi:literal ) => {
206+
$crate::abi::key($crate::abi::parse_or_fail($abi))
207+
};
208+
}

src/base.rs

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
use core::{
2+
fmt::{Debug, Pointer},
3+
hash::Hash,
4+
panic::{RefUnwindSafe, UnwindSafe},
5+
};
6+
7+
use crate::{abi::{AbiKey, Abi}, WithAbi, WithSafety, abi, make_unsafe, make_safe};
8+
9+
ffi_opaque::opaque! {
10+
/// A struct representing an opaque function.
11+
pub struct OpaqueFn;
12+
}
13+
14+
/// Type alias for a raw untyped function pointer.
15+
pub type UntypedFnPtr = *const OpaqueFn;
16+
17+
macro_rules! fnptr_trait_body {
18+
() => {
19+
/// The argument types as a tuple.
20+
type Args;
21+
22+
/// The return type.
23+
type Output;
24+
25+
/// The function's arity (number of arguments).
26+
const ARITY: usize;
27+
28+
/// Whether the function pointer is safe (fn) or unsafe (unsafe fn).
29+
const IS_SAFE: bool;
30+
31+
/// Whether the function pointer uses an extern calling convention.
32+
const IS_EXTERN: bool;
33+
34+
/// The ABI associated with this function pointer.
35+
const ABI: Abi;
36+
37+
/// Returns the address of this function.
38+
#[must_use]
39+
fn addr(&self) -> usize {
40+
self.as_ptr() as usize
41+
}
42+
/// Constructs an instance from an address.
43+
///
44+
/// # Safety
45+
/// This function is unsafe because it can not check if the argument points to a function
46+
/// of the correct type.
47+
#[must_use]
48+
unsafe fn from_addr(addr: usize) -> Self {
49+
unsafe { Self::from_ptr(addr as UntypedFnPtr) }
50+
}
51+
52+
/// Returns a untyped function pointer for this function.
53+
#[must_use]
54+
fn as_ptr(&self) -> UntypedFnPtr;
55+
/// Constructs an instance from an untyped function pointer.
56+
///
57+
/// # Safety
58+
/// This function is unsafe because it can not check if the argument points to a function
59+
/// of the correct type.
60+
#[must_use]
61+
unsafe fn from_ptr(ptr: UntypedFnPtr) -> Self;
62+
63+
/// Produces an unsafe version of this function pointer.
64+
#[must_use]
65+
fn as_unsafe(&self) -> make_unsafe!(Self) {
66+
unsafe { FnPtr::from_ptr(self.as_ptr()) }
67+
}
68+
69+
/// Produces a safe version of this function pointer.
70+
///
71+
/// # Safety
72+
/// Caller must ensure the underlying function is actually safe to call.
73+
#[must_use]
74+
unsafe fn as_safe(&self) -> make_safe!(Self) {
75+
unsafe { FnPtr::from_ptr(self.as_ptr()) }
76+
}
77+
78+
/// Produces a version of this function pointer with the given ABI.
79+
///
80+
/// # Safety
81+
/// Caller must ensure that the resulting ABI transformation is sound.
82+
#[must_use]
83+
unsafe fn with_abi<const ABI: AbiKey>(&self) -> <Self as WithAbi<ABI>>::F
84+
where
85+
Self: WithAbi<ABI>,
86+
{
87+
unsafe { FnPtr::from_ptr(self.as_ptr()) }
88+
}
89+
};
90+
}
91+
/// Marker trait for all function pointers.
92+
#[cfg(not(nightly_build))]
93+
pub trait FnPtr:
94+
PartialEq
95+
+ Eq
96+
+ PartialOrd
97+
+ Ord
98+
+ Hash
99+
+ Pointer
100+
+ Debug
101+
+ Clone
102+
+ Copy
103+
+ Send
104+
+ Sync
105+
+ Unpin
106+
+ UnwindSafe
107+
+ RefUnwindSafe
108+
+ Sized
109+
+ 'static
110+
+ WithSafety<true>
111+
+ WithSafety<false>
112+
+ WithAbi<{ abi!("Rust") }>
113+
+ WithAbi<{ abi!("C") }>
114+
+ WithAbi<{ abi!("system") }>
115+
{
116+
fnptr_trait_body!();
117+
}
118+
119+
/// Marker trait for all function pointers.
120+
#[cfg(nightly_build)]
121+
pub trait FnPtr:
122+
PartialEq
123+
+ Eq
124+
+ PartialOrd
125+
+ Ord
126+
+ Hash
127+
+ Pointer
128+
+ Debug
129+
+ Clone
130+
+ Copy
131+
+ Send
132+
+ Sync
133+
+ Unpin
134+
+ UnwindSafe
135+
+ RefUnwindSafe
136+
+ Sized
137+
+ 'static
138+
+ WithSafety<true>
139+
+ WithSafety<false>
140+
+ WithAbi<{ abi!("Rust") }>
141+
+ WithAbi<{ abi!("C") }>
142+
+ WithAbi<{ abi!("system") }>
143+
+ std::marker::FnPtr
144+
{
145+
fnptr_trait_body!();
146+
}
147+
148+
/// Marker trait for all *safe* function pointer types (`fn` / `extern fn`).
149+
pub trait SafeFnPtr: FnPtr {
150+
/// Invokes the function pointed to with the given args.
151+
///
152+
/// # Examples
153+
///
154+
/// ```
155+
/// # use fn_ptr::SafeFnPtr;
156+
/// fn add(a: i32, b: i32) -> i32 { a + b }
157+
///
158+
/// let f: fn(i32, i32) -> i32 = add;
159+
/// let result = f.invoke((2, 3));
160+
/// assert_eq!(result, 5);
161+
/// ```
162+
// NOTE: Can't use "call" due to fn_traits feature
163+
fn invoke(&self, args: Self::Args) -> Self::Output;
164+
}
165+
166+
/// Marker trait for all *unsafe* function pointer types (`unsafe fn` / `unsafe extern fn`).
167+
pub trait UnsafeFnPtr: FnPtr {
168+
/// Invokes the function pointed to with the given args.
169+
///
170+
/// # Safety
171+
/// Calling this function pointer is unsafe because the function may have
172+
/// invariants that must be manually upheld by the caller.
173+
///
174+
/// # Examples
175+
///
176+
/// ```
177+
/// # use fn_ptr::UnsafeFnPtr;
178+
/// unsafe fn add(a: *const i32, b: *const i32) -> i32 { *a + *b }
179+
///
180+
/// let f: unsafe fn(*const i32, *const i32) -> i32 = add;
181+
/// let result = unsafe { f.invoke((&2, &3)) };
182+
/// assert_eq!(result, 5);
183+
/// ```
184+
// NOTE: Can't use "call" due to fn_traits feature
185+
unsafe fn invoke(&self, args: Self::Args) -> Self::Output;
186+
}
187+
188+
/// Marker trait implemented for function pointers of a specific ABI.
189+
///
190+
/// For example:
191+
/// - `HasAbi<Abi::C>` for `extern "C" fn(...)`
192+
/// - `HasAbi<Abi::Sysv64>` for `extern "sysv64" fn(...)`
193+
pub trait HasAbi<const ABI: AbiKey>: FnPtr {}
194+
195+
/// Marker trait denoting the safety of a function pointer type.
196+
///
197+
/// For example:
198+
/// - `HasSafety<true>` for `extern "C" fn(...)`
199+
/// - `HasSafety<false>` for `unsafe fn(...)`
200+
pub trait HasSafety<const B: bool> {}
201+
impl<T: SafeFnPtr> HasSafety<true> for T {}
202+
impl<T: UnsafeFnPtr> HasSafety<false> for T {}

src/conversion.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
use crate::{FnPtr, HasAbi, HasSafety, abi::AbiKey};
2+
3+
/// Computes the function pointer type obtained by changing the ABI
4+
/// while preserving arity, arguments, return type, and safety.
5+
pub trait WithAbi<const ABI: AbiKey> {
6+
/// The function pointer type with the requested ABI (preserving safety and signature).
7+
type F: FnPtr + HasAbi<ABI>;
8+
}
9+
10+
/// Computes the function pointer type obtained by switching between safe/unsafe
11+
/// while preserving arity, ABI, and signature.
12+
pub trait WithSafety<const SAFE: bool> {
13+
/// The function pointer type with the requested safety (preserving ABI and signature).
14+
type F: FnPtr + HasSafety<SAFE>;
15+
}
16+
17+
18+
/// Construct a function-pointer type identical to the given one but using
19+
/// the specified ABI.
20+
///
21+
/// Accepts either:
22+
/// - an `Abi` value (e.g., `Abi::C`, `Abi::Sysv64`), or
23+
/// - a string literal (e.g., `"C"`, `"system"`, `"stdcall"`).
24+
///
25+
/// # Examples
26+
///
27+
/// ```rust
28+
/// # use fn_ptr::{with_abi, Abi};
29+
/// type F = extern "C" fn(i32) -> i32;
30+
///
31+
/// type G = with_abi!(Abi::Sysv64, F);
32+
/// // `G` is `extern "sysv64" fn(i32) -> i32`
33+
///
34+
/// type H = with_abi!("C", extern "system" fn());
35+
/// // `H` is `extern "C" fn()`
36+
/// ```
37+
#[macro_export]
38+
macro_rules! with_abi {
39+
// ABI given as a path (Abi::C, Abi::Sysv64, ...)
40+
( $abi:path, $ty:ty ) => {
41+
<$ty as $crate::WithAbi<{ $crate::abi::key($abi) }>>::F
42+
};
43+
44+
// ABI given as a string literal
45+
( $abi_lit:literal, $ty:ty ) => {
46+
<$ty as $crate::WithAbi<{ $crate::abi!($abi_lit) }>>::F
47+
};
48+
}
49+
50+
/// Convert a function-pointer type to the *safe* variant of the same
51+
/// signature. Arguments, return type, and ABI are preserved.
52+
///
53+
/// # Example
54+
///
55+
/// ```rust
56+
/// # use fn_ptr::make_safe;
57+
/// type U = unsafe extern "C" fn(i32);
58+
/// type S = make_safe!(U);
59+
/// // `S` is `extern "C" fn(i32)`
60+
/// ```
61+
#[macro_export]
62+
macro_rules! make_safe {
63+
( $ty:ty ) => {
64+
<$ty as $crate::WithSafety<{ true }>>::F
65+
};
66+
}
67+
68+
/// Convert a function-pointer type to the *unsafe* variant of the same
69+
/// signature. Arguments, return type, and ABI are preserved.
70+
///
71+
/// # Example
72+
///
73+
/// ```rust
74+
/// # use fn_ptr::make_unsafe;
75+
/// type S = extern "C" fn(i32);
76+
/// type U = make_unsafe!(S);
77+
/// // `U` is `unsafe extern "C" fn(i32)`
78+
/// ```
79+
#[macro_export]
80+
macro_rules! make_unsafe {
81+
( $ty:ty ) => {
82+
<$ty as $crate::WithSafety<{ false }>>::F
83+
};
84+
}
85+
86+
/// Convert a function-pointer type to an `extern` function that uses
87+
/// the specified ABI. Arguments, return type, and safety are preserved.
88+
///
89+
/// # Example
90+
///
91+
/// ```rust
92+
/// # use fn_ptr::{make_extern, Abi};
93+
/// type F = fn(i32) -> i32;
94+
/// type C = make_extern!(Abi::C, F);
95+
/// // `C` is `extern "C" fn(i32) -> i32`
96+
/// ```
97+
///
98+
/// Equivalent to:
99+
/// ```rust
100+
/// # use fn_ptr::{Abi, with_abi};
101+
/// # type F = fn(i32) -> i32;
102+
/// # type G =
103+
/// with_abi!(Abi::C, F)
104+
/// # ;
105+
/// ```
106+
#[macro_export]
107+
macro_rules! make_extern {
108+
( $abi:path, $ty:ty ) => {
109+
$crate::with_abi!($abi, $ty)
110+
};
111+
}
112+
113+
/// Convert a function-pointer type to a Rust-ABI (`fn`) function while
114+
/// preserving its arguments, return type, and safety.
115+
///
116+
/// # Example
117+
///
118+
/// ```rust
119+
/// # use fn_ptr::make_non_extern;
120+
/// type F = extern "C" fn(i32) -> i32;
121+
/// type R = make_non_extern!(F);
122+
/// // `R` is `fn(i32) -> i32`
123+
/// ```
124+
///
125+
/// Equivalent to:
126+
/// ```rust
127+
/// # use fn_ptr::{Abi, with_abi};
128+
/// # type F = extern "C" fn(i32) -> i32;
129+
/// # type G =
130+
/// with_abi!(Abi::Rust, F)
131+
/// # ;
132+
/// ```
133+
#[macro_export]
134+
macro_rules! make_non_extern {
135+
( $ty:ty ) => {
136+
$crate::with_abi!($crate::Abi::Rust, $ty)
137+
};
138+
}

0 commit comments

Comments
 (0)