use super::{FromArgs, FuncArgs};
use crate::{
convert::ToPyResult, object::PyThreadingConstraint, Py, PyPayload, PyRef, PyResult,
VirtualMachine,
};
use std::marker::PhantomData;
pub type PyNativeFn = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult);
pub trait IntoPyNativeFn<Kind>: Sized + PyThreadingConstraint + 'static {
fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult;
fn into_func(self) -> &'static PyNativeFn {
let boxed = Box::new(move |vm: &VirtualMachine, args| self.call(vm, args));
Box::leak(boxed)
}
}
impl<F, T, R, VM> IntoPyNativeFn<(T, R, VM)> for F
where
F: PyNativeFnInternal<T, R, VM>,
{
#[inline(always)]
fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
self.call_(vm, args)
}
}
mod sealed {
use super::*;
pub trait PyNativeFnInternal<T, R, VM>: Sized + PyThreadingConstraint + 'static {
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult;
}
}
use sealed::PyNativeFnInternal;
#[doc(hidden)]
pub struct OwnedParam<T>(PhantomData<T>);
#[doc(hidden)]
pub struct BorrowedParam<T>(PhantomData<T>);
#[doc(hidden)]
pub struct RefParam<T>(PhantomData<T>);
macro_rules! into_py_native_fn_tuple {
($(($n:tt, $T:ident)),*) => {
impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F
where
F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
$($T: FromArgs,)*
R: ToPyResult,
{
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
let ($($n,)*) = args.bind::<($($T,)*)>(vm)?;
(self)($($n,)* vm).to_pyresult(vm)
}
}
impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
where
F: Fn(&Py<S>, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
$($T: FromArgs,)*
R: ToPyResult,
{
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
(self)(&zelf, $($n,)* vm).to_pyresult(vm)
}
}
impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
where
F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
$($T: FromArgs,)*
R: ToPyResult,
{
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
(self)(&zelf, $($n,)* vm).to_pyresult(vm)
}
}
impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, ()> for F
where
F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static,
$($T: FromArgs,)*
R: ToPyResult,
{
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
let ($($n,)*) = args.bind::<($($T,)*)>(vm)?;
(self)($($n,)*).to_pyresult(vm)
}
}
impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
where
F: Fn(&Py<S>, $($T,)*) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
$($T: FromArgs,)*
R: ToPyResult,
{
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
(self)(&zelf, $($n,)*).to_pyresult(vm)
}
}
impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
where
F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
$($T: FromArgs,)*
R: ToPyResult,
{
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
(self)(&zelf, $($n,)*).to_pyresult(vm)
}
}
};
}
into_py_native_fn_tuple!();
into_py_native_fn_tuple!((v1, T1));
into_py_native_fn_tuple!((v1, T1), (v2, T2));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6));
into_py_native_fn_tuple!(
(v1, T1),
(v2, T2),
(v3, T3),
(v4, T4),
(v5, T5),
(v6, T6),
(v7, T7)
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_into_native_fn_noalloc() {
let check_zst = |f: &'static PyNativeFn| assert_eq!(std::mem::size_of_val(f), 0);
fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 {
1
}
check_zst(py_func.into_func());
let empty_closure = || "foo".to_owned();
check_zst(empty_closure.into_func());
}
}