Implement nl_meth as an union.
This commit is contained in:
parent
03e650c02f
commit
dadbc22b2e
|
@ -44,8 +44,8 @@ features = ["extension-module"]
|
|||
|
||||
**`src/lib.rs`**
|
||||
```rust
|
||||
use std::mem::transmute;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
|
||||
use pyo3_ffi::*;
|
||||
|
||||
|
@ -72,13 +72,11 @@ pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
|
|||
PyUnicode_FromStringAndSize(version.as_ptr() as *const c_char, version.len() as isize),
|
||||
);
|
||||
|
||||
// It's necessary to transmute `sum_as_string` here because functions marked with `METH_FASTCALL`
|
||||
// have a different signature. However the `PyMethodDef` struct currently represents all
|
||||
// functions as a `PyCFunction`. The python interpreter will cast the function pointer back
|
||||
// to `_PyCFunctionFast`.
|
||||
let wrapped_sum_as_string = PyMethodDef {
|
||||
ml_name: "sum_as_string\0".as_ptr() as *const c_char,
|
||||
ml_meth: Some(transmute::<_PyCFunctionFast, PyCFunction>(sum_as_string)),
|
||||
ml_meth: MlMeth {
|
||||
_PyCFunctionFast: Some(sum_as_string)
|
||||
},
|
||||
ml_flags: METH_FASTCALL,
|
||||
ml_doc: "returns the sum of two integers as a string\0".as_ptr() as *const c_char,
|
||||
};
|
||||
|
@ -127,7 +125,7 @@ pub unsafe extern "C" fn sum_as_string(
|
|||
|
||||
let arg1 = PyLong_AsLong(arg1);
|
||||
if !PyErr_Occurred().is_null() {
|
||||
return ptr::null()
|
||||
return ptr::null_mut()
|
||||
}
|
||||
|
||||
let arg2 = *args.add(1);
|
||||
|
@ -137,7 +135,7 @@ pub unsafe extern "C" fn sum_as_string(
|
|||
|
||||
let arg2 = PyLong_AsLong(arg2);
|
||||
if !PyErr_Occurred().is_null() {
|
||||
return ptr::null()
|
||||
return ptr::null_mut()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -83,8 +83,8 @@
|
|||
//!
|
||||
//! **`src/lib.rs`**
|
||||
//! ```rust
|
||||
//! use std::mem::transmute;
|
||||
//! use std::os::raw::c_char;
|
||||
//! use std::ptr;
|
||||
//!
|
||||
//! use pyo3_ffi::*;
|
||||
//!
|
||||
|
@ -111,13 +111,11 @@
|
|||
//! PyUnicode_FromStringAndSize(version.as_ptr() as *const c_char, version.len() as isize),
|
||||
//! );
|
||||
//!
|
||||
//! // It's necessary to transmute `sum_as_string` here because functions marked with `METH_FASTCALL`
|
||||
//! // have a different signature. However the `PyMethodDef` struct currently represents all
|
||||
//! // functions as a `PyCFunction`. The python interpreter will cast the function pointer back
|
||||
//! // to `_PyCFunctionFast`.
|
||||
//! let wrapped_sum_as_string = PyMethodDef {
|
||||
//! ml_name: "sum_as_string\0".as_ptr() as *const c_char,
|
||||
//! ml_meth: Some(transmute::<_PyCFunctionFast, PyCFunction>(sum_as_string)),
|
||||
//! ml_meth: MlMeth {
|
||||
//! _PyCFunctionFast: Some(sum_as_string)
|
||||
//! },
|
||||
//! ml_flags: METH_FASTCALL,
|
||||
//! ml_doc: "returns the sum of two integers as a string\0".as_ptr() as *const c_char,
|
||||
//! };
|
||||
|
@ -166,7 +164,7 @@
|
|||
//!
|
||||
//! let arg1 = PyLong_AsLong(arg1);
|
||||
//! if !PyErr_Occurred().is_null() {
|
||||
//! return ptr::null()
|
||||
//! return ptr::null_mut()
|
||||
//! }
|
||||
//!
|
||||
//! let arg2 = *args.add(1);
|
||||
|
@ -176,7 +174,7 @@
|
|||
//!
|
||||
//! let arg2 = PyLong_AsLong(arg2);
|
||||
//! if !PyErr_Occurred().is_null() {
|
||||
//! return ptr::null()
|
||||
//! return ptr::null_mut()
|
||||
//! }
|
||||
//! let res = (arg1 + arg2).to_string();
|
||||
//! PyUnicode_FromStringAndSize(res.as_ptr() as *const c_char, res.len() as isize)
|
||||
|
|
|
@ -31,7 +31,7 @@ pub unsafe fn PyCFunction_Check(op: *mut PyObject) -> c_int {
|
|||
pub type PyCFunction =
|
||||
unsafe extern "C" fn(slf: *mut PyObject, args: *mut PyObject) -> *mut PyObject;
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
|
||||
pub type _PyCFunctionFast = unsafe extern "C" fn(
|
||||
slf: *mut PyObject,
|
||||
args: *mut *mut PyObject,
|
||||
|
@ -52,7 +52,14 @@ pub type _PyCFunctionFastWithKeywords = unsafe extern "C" fn(
|
|||
kwnames: *mut PyObject,
|
||||
) -> *mut PyObject;
|
||||
|
||||
// skipped PyCMethod (since 3.9)
|
||||
#[cfg(all(Py_3_9, not(Py_LIMITED_API)))]
|
||||
pub type PyCMethod = unsafe extern "C" fn(
|
||||
slf: *mut PyObject,
|
||||
defining_class: *mut PyTypeObject,
|
||||
args: *const *mut PyObject,
|
||||
nargs: crate::pyport::Py_ssize_t,
|
||||
kwnames: *mut PyObject,
|
||||
) -> *mut PyObject;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyCFunction_GetFunction")]
|
||||
|
@ -71,11 +78,44 @@ extern "C" {
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct PyMethodDef {
|
||||
pub ml_name: *const c_char,
|
||||
pub ml_meth: Option<PyCFunction>,
|
||||
pub ml_meth: MlMeth,
|
||||
pub ml_flags: c_int,
|
||||
pub ml_doc: *const c_char,
|
||||
}
|
||||
|
||||
/// Function types used to implement Python callables.
|
||||
///
|
||||
/// This union must be accompanied by the correct [ml_flags](PyMethodDef::ml_flags),
|
||||
/// otherwise the behavior is undefined.
|
||||
///
|
||||
/// See the [Python C API documentation][1] for more information.
|
||||
///
|
||||
/// [1]: https://docs.python.org/3/c-api/structures.html#implementing-functions-and-methods
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union MlMeth {
|
||||
/// This variant corresponds with [`METH_VARARGS`] *or* [`METH_NOARGS`] *or* [`METH_O`].
|
||||
pub PyCFunction: Option<PyCFunction>,
|
||||
|
||||
/// This variant corresponds with [`METH_VARARGS`] | [`METH_KEYWORDS`].
|
||||
pub PyCFunctionWithKeywords: Option<PyCFunctionWithKeywords>,
|
||||
|
||||
/// This variant corresponds with [`METH_FASTCALL`].
|
||||
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
|
||||
pub _PyCFunctionFast: Option<_PyCFunctionFast>,
|
||||
|
||||
/// This variant corresponds with [`METH_FASTCALL`] | [`METH_KEYWORDS`].
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub _PyCFunctionFastWithKeywords: Option<_PyCFunctionFastWithKeywords>,
|
||||
|
||||
/// This variant corresponds with [`METH_METHOD`] | [`METH_FASTCALL`] | [`METH_KEYWORDS`].
|
||||
#[cfg(all(Py_3_9, not(Py_LIMITED_API)))]
|
||||
pub PyCMethod: Option<PyCMethod>,
|
||||
}
|
||||
|
||||
// TODO: This can be a const assert on Rust 1.57
|
||||
const _: () = [()][mem::size_of::<MlMeth>() - mem::size_of::<Option<extern "C" fn()>>()];
|
||||
|
||||
impl Default for PyMethodDef {
|
||||
fn default() -> PyMethodDef {
|
||||
unsafe { mem::zeroed() }
|
||||
|
@ -122,7 +162,9 @@ be specified alone or with METH_KEYWORDS. */
|
|||
pub const METH_FASTCALL: c_int = 0x0080;
|
||||
|
||||
// skipped METH_STACKLESS
|
||||
// skipped METH_METHOD
|
||||
|
||||
#[cfg(all(Py_3_9, not(Py_LIMITED_API)))]
|
||||
pub const METH_METHOD: c_int = 0x0200;
|
||||
|
||||
extern "C" {
|
||||
#[cfg(not(Py_3_9))]
|
||||
|
|
|
@ -163,17 +163,21 @@ impl PyMethodDef {
|
|||
/// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef`
|
||||
pub(crate) fn as_method_def(&self) -> Result<ffi::PyMethodDef, NulByteInString> {
|
||||
let meth = match self.ml_meth {
|
||||
PyMethodType::PyCFunction(meth) => meth.0,
|
||||
PyMethodType::PyCFunctionWithKeywords(meth) => unsafe { std::mem::transmute(meth.0) },
|
||||
PyMethodType::PyCFunction(meth) => ffi::MlMeth {
|
||||
PyCFunction: Some(meth.0),
|
||||
},
|
||||
PyMethodType::PyCFunctionWithKeywords(meth) => ffi::MlMeth {
|
||||
PyCFunctionWithKeywords: Some(meth.0),
|
||||
},
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
PyMethodType::PyCFunctionFastWithKeywords(meth) => unsafe {
|
||||
std::mem::transmute(meth.0)
|
||||
PyMethodType::PyCFunctionFastWithKeywords(meth) => ffi::MlMeth {
|
||||
_PyCFunctionFastWithKeywords: Some(meth.0),
|
||||
},
|
||||
};
|
||||
|
||||
Ok(ffi::PyMethodDef {
|
||||
ml_name: get_name(self.ml_name)?.as_ptr(),
|
||||
ml_meth: Some(meth),
|
||||
ml_meth: meth,
|
||||
ml_flags: self.ml_flags,
|
||||
ml_doc: get_doc(self.ml_doc)?.as_ptr(),
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue