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