1use super::{FromArgs, FuncArgs};
2use crate::{
3 Py, PyPayload, PyRef, PyResult, VirtualMachine, convert::ToPyResult,
4 object::PyThreadingConstraint,
5};
6use core::marker::PhantomData;
7
8pub trait PyNativeFn:
11 Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static
12{
13}
14impl<F: Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static> PyNativeFn
15 for F
16{
17}
18
19pub trait IntoPyNativeFn<Kind>: Sized + PyThreadingConstraint + 'static {
39 fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult;
40
41 fn into_func(self) -> impl PyNativeFn {
45 into_func(self)
46 }
47}
48
49const fn into_func<F: IntoPyNativeFn<Kind>, Kind>(f: F) -> impl PyNativeFn {
50 move |vm: &VirtualMachine, args| f.call(vm, args)
51}
52
53const fn zst_ref_out_of_thin_air<T: 'static>(x: T) -> &'static T {
54 core::mem::forget(x);
58 const {
59 if core::mem::size_of::<T>() != 0 {
60 panic!("can't use a non-zero-sized type here")
61 }
62 unsafe { core::ptr::NonNull::<T>::dangling().as_ref() }
65 }
66}
67
68#[inline(always)]
75pub const fn static_func<Kind, F: IntoPyNativeFn<Kind>>(f: F) -> &'static dyn PyNativeFn {
76 zst_ref_out_of_thin_air(into_func(f))
77}
78
79#[inline(always)]
80pub const fn static_raw_func<F: PyNativeFn>(f: F) -> &'static dyn PyNativeFn {
81 zst_ref_out_of_thin_air(f)
82}
83
84impl<F, T, R, VM> IntoPyNativeFn<(T, R, VM)> for F
87where
88 F: PyNativeFnInternal<T, R, VM>,
89{
90 #[inline(always)]
91 fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
92 self.call_(vm, args)
93 }
94}
95
96mod sealed {
97 use super::*;
98 pub trait PyNativeFnInternal<T, R, VM>: Sized + PyThreadingConstraint + 'static {
99 fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult;
100 }
101}
102use sealed::PyNativeFnInternal;
103
104#[doc(hidden)]
105pub struct OwnedParam<T>(PhantomData<T>);
106#[doc(hidden)]
107pub struct BorrowedParam<T>(PhantomData<T>);
108#[doc(hidden)]
109pub struct RefParam<T>(PhantomData<T>);
110
111macro_rules! into_py_native_fn_tuple {
116 ($(($n:tt, $T:ident)),*) => {
117 impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F
118 where
119 F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
120 $($T: FromArgs,)*
121 R: ToPyResult,
122 {
123 fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
124 let ($($n,)*) = args.bind::<($($T,)*)>(vm)?;
125
126 (self)($($n,)* vm).to_pyresult(vm)
127 }
128 }
129
130 impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
131 where
132 F: Fn(&Py<S>, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
133 S: PyPayload,
134 $($T: FromArgs,)*
135 R: ToPyResult,
136 {
137 fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
138 let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
139
140 (self)(&zelf, $($n,)* vm).to_pyresult(vm)
141 }
142 }
143
144 impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
145 where
146 F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
147 S: PyPayload,
148 $($T: FromArgs,)*
149 R: ToPyResult,
150 {
151 fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
152 let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
153
154 (self)(&zelf, $($n,)* vm).to_pyresult(vm)
155 }
156 }
157
158 impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, ()> for F
159 where
160 F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static,
161 $($T: FromArgs,)*
162 R: ToPyResult,
163 {
164 fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
165 let ($($n,)*) = args.bind::<($($T,)*)>(vm)?;
166
167 (self)($($n,)*).to_pyresult(vm)
168 }
169 }
170
171 impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
172 where
173 F: Fn(&Py<S>, $($T,)*) -> R + PyThreadingConstraint + 'static,
174 S: PyPayload,
175 $($T: FromArgs,)*
176 R: ToPyResult,
177 {
178 fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
179 let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
180
181 (self)(&zelf, $($n,)*).to_pyresult(vm)
182 }
183 }
184
185 impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
186 where
187 F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static,
188 S: PyPayload,
189 $($T: FromArgs,)*
190 R: ToPyResult,
191 {
192 fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
193 let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
194
195 (self)(&zelf, $($n,)*).to_pyresult(vm)
196 }
197 }
198 };
199}
200
201into_py_native_fn_tuple!();
202into_py_native_fn_tuple!((v1, T1));
203into_py_native_fn_tuple!((v1, T1), (v2, T2));
204into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3));
205into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4));
206into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5));
207into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6));
208into_py_native_fn_tuple!(
209 (v1, T1),
210 (v2, T2),
211 (v3, T3),
212 (v4, T4),
213 (v5, T5),
214 (v6, T6),
215 (v7, T7)
216);
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use core::mem::size_of_val;
222
223 #[test]
224 fn test_into_native_fn_noalloc() {
225 fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 {
226 1
227 }
228 assert_eq!(size_of_val(&py_func.into_func()), 0);
229 let empty_closure = || "foo".to_owned();
230 assert_eq!(size_of_val(&empty_closure.into_func()), 0);
231 assert_eq!(size_of_val(static_func(empty_closure)), 0);
232 }
233}