use core::{
marker::PhantomData,
ops::Deref,
ptr::addr_of,
sync::atomic::{AtomicUsize, Ordering},
};
#[macro_export]
macro_rules! function {
(
$(
$(extern $($abi:literal)?)? fn $fname:ident( $($atype:ty),* ) $(-> $ret:ty)? = $( ($resolver:path) )? $modname:literal $sep:tt $offset:expr;
)*
) => {
$(
static $fname: $crate::Function< $(extern $($abi)?)? fn($($atype),*) $(-> $ret)?> = $crate::Function::new(
|| unsafe {
($crate::__resolver!( $($resolver)? ))( $crate::__resolve_by!($sep $modname, $offset ) )
}
);
)*
};
}
pub struct Function<F> {
address: AtomicUsize,
init: fn() -> usize,
_ph: PhantomData<F>,
}
impl<F> Function<F> {
#[doc(hidden)]
pub const fn new(init: fn() -> usize) -> Self {
Self {
address: AtomicUsize::new(0),
init,
_ph: PhantomData,
}
}
pub fn address(&self) -> usize {
let mut address = self.address.load(Ordering::Acquire);
if address == 0 {
address = (self.init)();
self.address.store(address, Ordering::Release);
}
address
}
pub fn force(&self) {
_ = self.address();
}
}
impl<F> Deref for Function<F> {
type Target = F;
fn deref(&self) -> &Self::Target {
self.force();
unsafe {
addr_of!(self.address)
.cast::<Self::Target>()
.as_ref()
.unwrap()
}
}
}