use super::{FromArgs, FuncArgs};
use crate::{
convert::ToPyResult, object::PyThreadingConstraint, Py, PyPayload, PyRef, PyResult,
VirtualMachine,
};
use std::marker::PhantomData;
pub trait PyNativeFn:
Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static
{
}
impl<F: Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static> PyNativeFn
for F
{
}
pub trait IntoPyNativeFn<Kind>: Sized + PyThreadingConstraint + 'static {
fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult;
fn into_func(self) -> impl PyNativeFn {
into_func(self)
}
}
const fn into_func<F: IntoPyNativeFn<Kind>, Kind>(f: F) -> impl PyNativeFn {
move |vm: &VirtualMachine, args| f.call(vm, args)
}
const fn zst_ref_out_of_thin_air<T: 'static>(x: T) -> &'static T {
std::mem::forget(x);
trait Zst: Sized + 'static {
const THIN_AIR: &'static Self = {
if std::mem::size_of::<Self>() == 0 {
unsafe { std::ptr::NonNull::<Self>::dangling().as_ref() }
} else {
panic!("can't use a non-zero-sized type here")
}
};
}
impl<T: 'static> Zst for T {}
<T as Zst>::THIN_AIR
}
#[inline(always)]
pub const fn static_func<Kind, F: IntoPyNativeFn<Kind>>(f: F) -> &'static dyn PyNativeFn {
zst_ref_out_of_thin_air(into_func(f))
}
#[inline(always)]
pub const fn static_raw_func<F: PyNativeFn>(f: F) -> &'static dyn PyNativeFn {
zst_ref_out_of_thin_air(f)
}
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::*;
use std::mem::size_of_val;
#[test]
fn test_into_native_fn_noalloc() {
fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 {
1
}
assert_eq!(size_of_val(&py_func.into_func()), 0);
let empty_closure = || "foo".to_owned();
assert_eq!(size_of_val(&empty_closure.into_func()), 0);
assert_eq!(size_of_val(static_func(empty_closure)), 0);
}
}