gh-61103: Support double complex (_Complex) type in ctypes (#120894)

Example:

```pycon
>>> import ctypes
>>> ctypes.__STDC_IEC_559_COMPLEX__
1
>>> libm = ctypes.CDLL('libm.so.6')
>>> libm.clog.argtypes = [ctypes.c_double_complex]
>>> libm.clog.restype = ctypes.c_double_complex
>>> libm.clog(1+1j)
(0.34657359027997264+0.7853981633974483j)
```

Co-authored-by: Nice Zombies <nineteendo19d0@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
Sergey B Kirpichev 2024-07-01 11:54:33 +03:00 committed by GitHub
parent a0b8b342c5
commit 6988ff02a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 316 additions and 17 deletions

34
Modules/_complex.h Normal file
View file

@ -0,0 +1,34 @@
/* Workarounds for buggy complex number arithmetic implementations. */
#ifndef Py_HAVE_C_COMPLEX
# error "this header file should only be included if Py_HAVE_C_COMPLEX is defined"
#endif
#include <complex.h>
/* Other compilers (than clang), that claims to
implement C11 *and* define __STDC_IEC_559_COMPLEX__ - don't have
issue with CMPLX(). This is specific to glibc & clang combination:
https://sourceware.org/bugzilla/show_bug.cgi?id=26287
Here we fallback to using __builtin_complex(), available in clang
v12+. Else CMPLX implemented following C11 6.2.5p13: "Each complex type
has the same representation and alignment requirements as an array
type containing exactly two elements of the corresponding real type;
the first element is equal to the real part, and the second element
to the imaginary part, of the complex number.
*/
#if !defined(CMPLX)
# if defined(__clang__) && __has_builtin(__builtin_complex)
# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y))
# else
static inline double complex
CMPLX(double real, double imag)
{
double complex z;
((double *)(&z))[0] = real;
((double *)(&z))[1] = imag;
return z;
}
# endif
#endif

View file

@ -1750,7 +1750,11 @@ class _ctypes.c_void_p "PyObject *" "clinic_state_sub()->PyCSimpleType_Type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=dd4d9646c56f43a9]*/
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCfuzZqQPXOv?g";
#else
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
#endif
/*[clinic input]
_ctypes.c_wchar_p.from_param as c_wchar_p_from_param
@ -2226,7 +2230,17 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
goto error;
}
stginfo->ffi_type_pointer = *fmt->pffi_type;
if (!fmt->pffi_type->elements) {
stginfo->ffi_type_pointer = *fmt->pffi_type;
}
else {
stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
stginfo->ffi_type_pointer.elements = PyMem_Malloc(2 * sizeof(ffi_type));
memcpy(stginfo->ffi_type_pointer.elements,
fmt->pffi_type->elements, 2 * sizeof(ffi_type));
}
stginfo->align = fmt->pffi_type->alignment;
stginfo->length = 0;
stginfo->size = fmt->pffi_type->size;

View file

@ -13,6 +13,12 @@
#include <Python.h>
#include <ffi.h> // FFI_TARGET_HAS_COMPLEX_TYPE
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
# include "../_complex.h" // csqrt()
# undef I // for _ctypes_test_generated.c.h
#endif
#include <stdio.h> // printf()
#include <stdlib.h> // qsort()
#include <string.h> // memset()
@ -443,6 +449,13 @@ EXPORT(double) my_sqrt(double a)
return sqrt(a);
}
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
EXPORT(double complex) my_csqrt(double complex a)
{
return csqrt(a);
}
#endif
EXPORT(void) my_qsort(void *base, size_t num, size_t width, int(*compare)(const void*, const void*))
{
qsort(base, num, width, compare);

View file

@ -105,6 +105,10 @@ module _ctypes
#include "pycore_global_objects.h"// _Py_ID()
#include "pycore_traceback.h" // _PyTraceback_Add()
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
#include "../_complex.h" // complex
#endif
#include "clinic/callproc.c.h"
#define CTYPES_CAPSULE_NAME_PYMEM "_ctypes pymem"
@ -651,6 +655,9 @@ union result {
double d;
float f;
void *p;
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
double complex C;
#endif
};
struct argument {

View file

@ -14,6 +14,9 @@
#include <ffi.h>
#include "ctypes.h"
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
# include "../_complex.h" // complex
#endif
#define CTYPES_CFIELD_CAPSULE_NAME_PYMEM "_ctypes/cfield.c pymem"
@ -1087,6 +1090,30 @@ d_get(void *ptr, Py_ssize_t size)
return PyFloat_FromDouble(val);
}
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
static PyObject *
C_set(void *ptr, PyObject *value, Py_ssize_t size)
{
Py_complex c = PyComplex_AsCComplex(value);
if (c.real == -1 && PyErr_Occurred()) {
return NULL;
}
double complex x = CMPLX(c.real, c.imag);
memcpy(ptr, &x, sizeof(x));
_RET(value);
}
static PyObject *
C_get(void *ptr, Py_ssize_t size)
{
double complex x;
memcpy(&x, ptr, sizeof(x));
return PyComplex_FromDoubles(creal(x), cimag(x));
}
#endif
static PyObject *
d_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
{
@ -1592,6 +1619,9 @@ static struct fielddesc formattable[] = {
{ 'B', B_set, B_get, NULL},
{ 'c', c_set, c_get, NULL},
{ 'd', d_set, d_get, NULL, d_set_sw, d_get_sw},
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
{ 'C', C_set, C_get, NULL},
#endif
{ 'g', g_set, g_get, NULL},
{ 'f', f_set, f_get, NULL, f_set_sw, f_get_sw},
{ 'h', h_set, h_get, NULL, h_set_sw, h_get_sw},
@ -1642,6 +1672,9 @@ _ctypes_init_fielddesc(void)
case 'B': fd->pffi_type = &ffi_type_uchar; break;
case 'c': fd->pffi_type = &ffi_type_schar; break;
case 'd': fd->pffi_type = &ffi_type_double; break;
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
case 'C': fd->pffi_type = &ffi_type_complex_double; break;
#endif
case 'g': fd->pffi_type = &ffi_type_longdouble; break;
case 'f': fd->pffi_type = &ffi_type_float; break;
case 'h': fd->pffi_type = &ffi_type_sshort; break;

View file

@ -2,9 +2,15 @@
# include <alloca.h>
#endif
#include <ffi.h> // FFI_TARGET_HAS_COMPLEX_TYPE
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_typeobject.h" // _PyType_GetModuleState()
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
# include "../_complex.h" // complex
#endif
#ifndef MS_WIN32
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
@ -393,6 +399,9 @@ struct tagPyCArgObject {
double d;
float f;
void *p;
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
double complex C;
#endif
} value;
PyObject *obj;
Py_ssize_t size; /* for the 'V' tag */