#[macro_export]
macro_rules! export_ppp_callback {
{
$(
$vis:vis fn $cb_name:ident (
$(
$arg:ident : $arg_ty:ty
),* $(,)?
) $(-> $ret_ty:ty)?;
)*
} => {$(
$vis mod $cb_name {
use super::*;
use ::std::ffi::c_void;
#[derive(PartialEq)]
struct PppContextInternal(*mut c_void);
unsafe impl Sync for PppContextInternal {}
unsafe impl Send for PppContextInternal {}
$vis type CallbackType = extern "C" fn($( $arg_ty ),*) $(-> $ret_ty)?;
$vis type CallbackTypeWithContext = extern "C" fn(*mut c_void, $( $arg_ty ),*) $(-> $ret_ty)?;
extern "C" fn trampoline(context: *mut c_void, $($arg : $arg_ty),*) $(-> $ret_ty)? {
let cb: CallbackType = unsafe {
::core::mem::transmute(context)
};
cb($($arg),*)
}
#[export_name = concat!("ppp_add_cb_", stringify!($cb_name))]
$vis extern "C" fn add_callback(callback: CallbackType) {
unsafe {
add_callback_with_context(trampoline, ::core::mem::transmute(callback))
}
}
#[export_name = concat!("ppp_add_cb_", stringify!($cb_name), "_with_context")]
$vis extern "C" fn add_callback_with_context(
callback: CallbackTypeWithContext,
context: *mut c_void,
) {
CALLBACKS
.lock()
.unwrap()
.push((callback, PppContextInternal(context)));
}
#[export_name = concat!("ppp_remove_cb_", stringify!($cb_name))]
$vis extern "C" fn remove_callback(callback: CallbackType) -> bool {
unsafe {
remove_callback_with_context(trampoline, ::core::mem::transmute(callback))
}
}
#[export_name = concat!("ppp_remove_cb_", stringify!($cb_name), "_with_context")]
$vis extern "C" fn remove_callback_with_context(
callback: CallbackTypeWithContext,
context: *mut c_void,
) -> bool {
let context = PppContextInternal(context);
let mut callbacks = CALLBACKS.lock().unwrap();
let old_len = callbacks.len();
callbacks.retain(
|(cb, cb_ctxt)| (*cb as usize, cb_ctxt) != (callback as _, &context)
);
callbacks.len() != old_len
}
$crate::lazy_static::lazy_static! {
static ref CALLBACKS: ::std::sync::Mutex<
Vec<(CallbackTypeWithContext, PppContextInternal)>
> = ::std::sync::Mutex::new(Vec::new());
}
$vis fn trigger($($arg : $arg_ty),*) $(-> $ret_ty)? {
CALLBACKS.lock()
.unwrap()
.iter_mut()
.map(|(callback, PppContextInternal(context))| callback(
*context,
$($arg),*
))
.fold(
$crate::__callback_fold_default!($($ret_ty)?),
$crate::__callback_fold_fn!($($ret_ty)?)
)
}
}
)*};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __callback_fold_default {
() => {
()
};
($ty:ty) => {
<$ty as $crate::CallbackReturn>::callback_fold_default()
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __callback_fold_fn {
() => {
(|(), _| ())
};
($ty:ty) => {
<$ty as $crate::CallbackReturn>::fold_callback_return
};
}
pub trait CallbackReturn {
type FoldType: Default;
fn fold_callback_return(folded: Self::FoldType, ret: Self) -> Self::FoldType;
fn callback_fold_default() -> Self::FoldType {
Self::FoldType::default()
}
}
impl CallbackReturn for bool {
type FoldType = bool;
fn fold_callback_return(folded: Self::FoldType, ret: Self) -> Self::FoldType {
folded | ret
}
}
macro_rules! impl_for_ints {
($($ty:ty)*) => {
$(
impl CallbackReturn for $ty {
type FoldType = $ty;
fn fold_callback_return(folded: Self::FoldType, ret: Self) -> Self::FoldType {
if folded != 0 {
folded
} else {
ret
}
}
}
)*
};
}
impl_for_ints!(u8 u16 u32 u64 usize i8 i16 i32 i64 isize);