mirror of
https://github.com/python/cpython.git
synced 2024-11-21 12:59:38 +01:00
gh-61103: Support float and long double complex types in ctypes module (#121248)
This amends 6988ff02a5
: memory allocation for
stginfo->ffi_type_pointer.elements in PyCSimpleType_init() should be
more generic (perhaps someday fmt->pffi_type->elements will be not a
two-elements array).
It should finally resolve #61103.
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
c9bdfbe868
commit
51c4a324c0
@ -272,8 +272,12 @@ complex types are available:
|
||||
+----------------------------------+---------------------------------+-----------------+
|
||||
| ctypes type | C type | Python type |
|
||||
+==================================+=================================+=================+
|
||||
| :class:`c_float_complex` | :c:expr:`float complex` | complex |
|
||||
+----------------------------------+---------------------------------+-----------------+
|
||||
| :class:`c_double_complex` | :c:expr:`double complex` | complex |
|
||||
+----------------------------------+---------------------------------+-----------------+
|
||||
| :class:`c_longdouble_complex` | :c:expr:`long double complex` | complex |
|
||||
+----------------------------------+---------------------------------+-----------------+
|
||||
|
||||
|
||||
All these types can be created by calling them with an optional initializer of
|
||||
@ -2302,6 +2306,22 @@ These are the fundamental ctypes data types:
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
.. class:: c_float_complex
|
||||
|
||||
Represents the C :c:expr:`float complex` datatype, if available. The
|
||||
constructor accepts an optional :class:`complex` initializer.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
.. class:: c_longdouble_complex
|
||||
|
||||
Represents the C :c:expr:`long double complex` datatype, if available. The
|
||||
constructor accepts an optional :class:`complex` initializer.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
.. class:: c_int
|
||||
|
||||
Represents the C :c:expr:`signed int` datatype. The constructor accepts an
|
||||
|
@ -208,6 +208,10 @@ if sizeof(c_longdouble) == sizeof(c_double):
|
||||
try:
|
||||
class c_double_complex(_SimpleCData):
|
||||
_type_ = "C"
|
||||
class c_float_complex(_SimpleCData):
|
||||
_type_ = "E"
|
||||
class c_longdouble_complex(_SimpleCData):
|
||||
_type_ = "F"
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
@ -33,6 +33,20 @@ class LibTest(unittest.TestCase):
|
||||
self.assertAlmostEqual(lib.my_csqrt(-1-0.01j),
|
||||
0.004999937502734214-1.0000124996093955j)
|
||||
|
||||
lib.my_csqrtf.argtypes = ctypes.c_float_complex,
|
||||
lib.my_csqrtf.restype = ctypes.c_float_complex
|
||||
self.assertAlmostEqual(lib.my_csqrtf(-1+0.01j),
|
||||
0.004999937502734214+1.0000124996093955j)
|
||||
self.assertAlmostEqual(lib.my_csqrtf(-1-0.01j),
|
||||
0.004999937502734214-1.0000124996093955j)
|
||||
|
||||
lib.my_csqrtl.argtypes = ctypes.c_longdouble_complex,
|
||||
lib.my_csqrtl.restype = ctypes.c_longdouble_complex
|
||||
self.assertAlmostEqual(lib.my_csqrtl(-1+0.01j),
|
||||
0.004999937502734214+1.0000124996093955j)
|
||||
self.assertAlmostEqual(lib.my_csqrtl(-1-0.01j),
|
||||
0.004999937502734214-1.0000124996093955j)
|
||||
|
||||
def test_qsort(self):
|
||||
comparefunc = CFUNCTYPE(c_int, POINTER(c_char), POINTER(c_char))
|
||||
lib.my_qsort.argtypes = c_void_p, c_size_t, c_size_t, comparefunc
|
||||
|
@ -146,7 +146,8 @@ class NumberTestCase(unittest.TestCase):
|
||||
@unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
|
||||
"requires C11 complex type")
|
||||
def test_complex(self):
|
||||
for t in [ctypes.c_double_complex]:
|
||||
for t in [ctypes.c_double_complex, ctypes.c_float_complex,
|
||||
ctypes.c_longdouble_complex]:
|
||||
self.assertEqual(t(1).value, 1+0j)
|
||||
self.assertEqual(t(1.0).value, 1+0j)
|
||||
self.assertEqual(t(1+0.125j).value, 1+0.125j)
|
||||
@ -162,9 +163,10 @@ class NumberTestCase(unittest.TestCase):
|
||||
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
|
||||
-3, INF, -INF, NAN], 2)]
|
||||
for z in values:
|
||||
with self.subTest(z=z):
|
||||
z2 = ctypes.c_double_complex(z).value
|
||||
self.assertComplexesAreIdentical(z, z2)
|
||||
for t in [ctypes.c_double_complex, ctypes.c_float_complex,
|
||||
ctypes.c_longdouble_complex]:
|
||||
with self.subTest(z=z, type=t):
|
||||
self.assertComplexesAreIdentical(z, t(z).value)
|
||||
|
||||
def test_integers(self):
|
||||
f = FloatLike()
|
||||
|
@ -1,3 +1,5 @@
|
||||
Support :c:expr:`double complex` C type in :mod:`ctypes` via
|
||||
:class:`~ctypes.c_double_complex` if compiler has C11 complex
|
||||
arithmetic. Patch by Sergey B Kirpichev.
|
||||
Support :c:expr:`float complex`, :c:expr:`double complex` and
|
||||
:c:expr:`long double complex` C types in :mod:`ctypes` as
|
||||
:class:`~ctypes.c_float_complex`, :class:`~ctypes.c_double_complex` and
|
||||
:class:`~ctypes.c_longdouble_complex` if the compiler has C11 complex arithmetic.
|
||||
Patch by Sergey B Kirpichev.
|
||||
|
@ -21,6 +21,8 @@
|
||||
#if !defined(CMPLX)
|
||||
# if defined(__clang__) && __has_builtin(__builtin_complex)
|
||||
# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y))
|
||||
# define CMPLXF(x, y) __builtin_complex ((float) (x), (float) (y))
|
||||
# define CMPLXL(x, y) __builtin_complex ((long double) (x), (long double) (y))
|
||||
# else
|
||||
static inline double complex
|
||||
CMPLX(double real, double imag)
|
||||
@ -30,5 +32,23 @@ CMPLX(double real, double imag)
|
||||
((double *)(&z))[1] = imag;
|
||||
return z;
|
||||
}
|
||||
|
||||
static inline float complex
|
||||
CMPLXF(float real, float imag)
|
||||
{
|
||||
float complex z;
|
||||
((float *)(&z))[0] = real;
|
||||
((float *)(&z))[1] = imag;
|
||||
return z;
|
||||
}
|
||||
|
||||
static inline long double complex
|
||||
CMPLXL(long double real, long double imag)
|
||||
{
|
||||
long double complex z;
|
||||
((long double *)(&z))[0] = real;
|
||||
((long double *)(&z))[1] = imag;
|
||||
return z;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
@ -1751,7 +1751,7 @@ class _ctypes.c_void_p "PyObject *" "clinic_state_sub()->PyCSimpleType_Type"
|
||||
/*[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";
|
||||
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCEFfuzZqQPXOv?g";
|
||||
#else
|
||||
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
|
||||
#endif
|
||||
@ -2234,12 +2234,13 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
stginfo->ffi_type_pointer = *fmt->pffi_type;
|
||||
}
|
||||
else {
|
||||
const size_t els_size = sizeof(fmt->pffi_type->elements);
|
||||
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));
|
||||
stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size);
|
||||
memcpy(stginfo->ffi_type_pointer.elements,
|
||||
fmt->pffi_type->elements, 2 * sizeof(ffi_type));
|
||||
fmt->pffi_type->elements, els_size);
|
||||
}
|
||||
stginfo->align = fmt->pffi_type->alignment;
|
||||
stginfo->length = 0;
|
||||
|
@ -454,6 +454,16 @@ EXPORT(double complex) my_csqrt(double complex a)
|
||||
{
|
||||
return csqrt(a);
|
||||
}
|
||||
|
||||
EXPORT(float complex) my_csqrtf(float complex a)
|
||||
{
|
||||
return csqrtf(a);
|
||||
}
|
||||
|
||||
EXPORT(long double complex) my_csqrtl(long double complex a)
|
||||
{
|
||||
return csqrtl(a);
|
||||
}
|
||||
#endif
|
||||
|
||||
EXPORT(void) my_qsort(void *base, size_t num, size_t width, int(*compare)(const void*, const void*))
|
||||
|
@ -657,6 +657,8 @@ union result {
|
||||
void *p;
|
||||
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
||||
double complex C;
|
||||
float complex E;
|
||||
long double complex F;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -1112,6 +1112,50 @@ C_get(void *ptr, Py_ssize_t size)
|
||||
memcpy(&x, ptr, sizeof(x));
|
||||
return PyComplex_FromDoubles(creal(x), cimag(x));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
E_set(void *ptr, PyObject *value, Py_ssize_t size)
|
||||
{
|
||||
Py_complex c = PyComplex_AsCComplex(value);
|
||||
|
||||
if (c.real == -1 && PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
float complex x = CMPLXF((float)c.real, (float)c.imag);
|
||||
memcpy(ptr, &x, sizeof(x));
|
||||
_RET(value);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
E_get(void *ptr, Py_ssize_t size)
|
||||
{
|
||||
float complex x;
|
||||
|
||||
memcpy(&x, ptr, sizeof(x));
|
||||
return PyComplex_FromDoubles(crealf(x), cimagf(x));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
F_set(void *ptr, PyObject *value, Py_ssize_t size)
|
||||
{
|
||||
Py_complex c = PyComplex_AsCComplex(value);
|
||||
|
||||
if (c.real == -1 && PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
long double complex x = CMPLXL(c.real, c.imag);
|
||||
memcpy(ptr, &x, sizeof(x));
|
||||
_RET(value);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
F_get(void *ptr, Py_ssize_t size)
|
||||
{
|
||||
long double complex x;
|
||||
|
||||
memcpy(&x, ptr, sizeof(x));
|
||||
return PyComplex_FromDoubles((double)creall(x), (double)cimagl(x));
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
@ -1621,6 +1665,8 @@ static struct fielddesc formattable[] = {
|
||||
{ '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},
|
||||
{ 'E', E_set, E_get, NULL},
|
||||
{ 'F', F_set, F_get, NULL},
|
||||
#endif
|
||||
{ 'g', g_set, g_get, NULL},
|
||||
{ 'f', f_set, f_get, NULL, f_set_sw, f_get_sw},
|
||||
@ -1674,6 +1720,8 @@ _ctypes_init_fielddesc(void)
|
||||
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;
|
||||
case 'E': fd->pffi_type = &ffi_type_complex_float; break;
|
||||
case 'F': fd->pffi_type = &ffi_type_complex_longdouble; break;
|
||||
#endif
|
||||
case 'g': fd->pffi_type = &ffi_type_longdouble; break;
|
||||
case 'f': fd->pffi_type = &ffi_type_float; break;
|
||||
|
@ -401,6 +401,8 @@ struct tagPyCArgObject {
|
||||
void *p;
|
||||
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
||||
double complex C;
|
||||
float complex E;
|
||||
long double complex F;
|
||||
#endif
|
||||
} value;
|
||||
PyObject *obj;
|
||||
|
Loading…
Reference in New Issue
Block a user