use crate::{
num::Integer,
primitive::Primitive,
ptr::{Pointer, cast_ptr},
tuple::Tuple,
util::cast_int,
};
pub trait FunctionPointer: Primitive + Copy + Sized {
type Args: Tuple;
type Return;
fn call(self, args: Self::Args) -> Self::Return;
fn cast_ptr<T: Sized, P: Pointer<T>>(self) -> P;
fn cast_int<T: Integer>(self) -> T;
}
impl<R> Primitive for fn() -> R {}
impl<R> FunctionPointer for fn() -> R {
type Args = ();
type Return = R;
fn call(self, _args: Self::Args) -> Self::Return {
self()
}
fn cast_ptr<T: Sized, P: Pointer<T>>(self) -> P {
cast_ptr!(T, P, self)
}
fn cast_int<T: Integer>(self) -> T {
cast_int!(self => T)
}
}
#[cfg_attr(docsrs, doc(fake_variadic))]
#[cfg_attr(
docsrs,
doc = "This trait is implemented for function pointers with up to 12 arguments."
)]
impl<A1, R> Primitive for fn(A1) -> R {}
#[cfg_attr(docsrs, doc(fake_variadic))]
#[cfg_attr(
docsrs,
doc = "This trait is implemented for function pointers with up to 12 arguments."
)]
impl<A1, R> FunctionPointer for fn(A1) -> R {
type Args = (A1,);
type Return = R;
fn call(self, args: Self::Args) -> Self::Return {
self(args.0)
}
fn cast_ptr<T: Sized, P: Pointer<T>>(self) -> P {
cast_ptr!(T, P, self)
}
fn cast_int<T: Integer>(self) -> T {
cast_int!(self => T)
}
}
macro_rules! impl_fn {
($($args:tt $n:tt),*) => {
#[cfg_attr(docsrs, doc(hidden))]
impl<$($args,)* R> Primitive for fn($($args,)*) -> R {}
#[cfg_attr(docsrs, doc(hidden))]
impl<$($args,)* R> FunctionPointer for fn($($args,)*) -> R {
type Args = ($($args,)*);
type Return = R;
fn call(self, args: Self::Args) -> Self::Return {
self($(args.$n),*)
}
fn cast_ptr<T: Sized, P: Pointer<T>>(self) -> P {
cast_ptr!(T, P, self)
}
fn cast_int<T: Integer>(self) -> T {
cast_int!(self => T)
}
}
}
}
impl_fn!(A1 0, A2 1);
impl_fn!(A1 0, A2 1, A3 2);
impl_fn!(A1 0, A2 1, A3 2, A4 3);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4, A6 5);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4, A6 5, A7 6);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4, A6 5, A7 6, A8 7);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4, A6 5, A7 6, A8 7, A9 8);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4, A6 5, A7 6, A8 7, A9 8, A10 9);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4, A6 5, A7 6, A8 7, A9 8, A10 9, A11 10);
impl_fn!(A1 0, A2 1, A3 2, A4 3, A5 4, A6 5, A7 6, A8 7, A9 8, A10 9, A11 10, A12 11);
#[cfg(test)]
mod test {
use super::FunctionPointer;
fn f0() {}
fn f1<T>(a: T) -> T {
a
}
fn f2<T1, T2>(a: T1, b: T2) -> (T1, T2) {
(a, b)
}
fn f3<T1, T2, T3>(a: T1, b: T2, c: T3) -> (T1, T2, T3) {
(a, b, c)
}
#[test]
fn test_call_0_args() {
(f0 as fn() -> _).call(());
}
#[test]
fn test_call_1_arg() {
assert_eq!((f1 as fn(usize) -> _).call((1337,)), (1337usize));
}
#[test]
fn test_call_2_args() {
assert_eq!(
(f2 as fn(char, [u8; 2]) -> _).call(('x', [2, 3])),
('x', [2u8, 3u8])
);
}
#[test]
fn test_call_3_args() {
assert_eq!(
(f3 as fn(usize, &'static str, bool) -> _).call((1, "b", false)),
(1usize, "b", false)
);
}
}