]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-61103: Support float and long double complex types in ctypes module (#121248)
authorSergey B Kirpichev <skirpichev@gmail.com>
Wed, 3 Jul 2024 09:08:11 +0000 (12:08 +0300)
committerGitHub <noreply@github.com>
Wed, 3 Jul 2024 09:08:11 +0000 (11:08 +0200)
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>
Doc/library/ctypes.rst
Lib/ctypes/__init__.py
Lib/test/test_ctypes/test_libc.py
Lib/test/test_ctypes/test_numbers.py
Misc/NEWS.d/next/Library/2024-06-23-07-23-08.gh-issue-61103.ca_U_l.rst
Modules/_complex.h
Modules/_ctypes/_ctypes.c
Modules/_ctypes/_ctypes_test.c
Modules/_ctypes/callproc.c
Modules/_ctypes/cfield.c
Modules/_ctypes/ctypes.h

index a56e0eef5d11b1fd3900b2ca82a38d520dc7a583..e3d74d7dc0d91c5fb443e711b838934946afb549 100644 (file)
@@ -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
index d2e6a8bfc8c9d46d378dd8563b69ddadf56b6359..721522caeeac92ab2fc8225f60a8dde8a56f2c5e 100644 (file)
@@ -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
 
index dec0afff4b38fd300c276fa95a69a35608df7c9b..cab3cc9f46003a3332e385456cd2780c50250339 100644 (file)
@@ -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
index b3816f61a6e7aadc1fb7cc30e80753be6eff4c7c..1dd3f2a234b1ee36b59e0cb3faf3c2626b5dd0f1 100644 (file)
@@ -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()
index 7b11d8c303c2f6d19b25800ff1e8e5fc431deb38..890eb62010eb335d26a49c8a60a5944355570015 100644 (file)
@@ -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.
index 1c1d1c8cae51b909d93f6ee08501651cd7893a6c..28d4a32794b97c079f373ffa7349940b6bec8c1d 100644 (file)
@@ -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
index 3647361b13a52c683c83be1ab361c66698a0a357..db58f33511c166fbb1a0915dbd84ff6586dd1bdc 100644 (file)
@@ -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;
index cbc8f8b0b453af44cb8d578ab06c0a7c32f1aabf..b8e613fd669d1bc5b754d3c7173fd56cdeeb287e 100644 (file)
@@ -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*))
index a83fa19af32402fbef66ab5596215e16d51ca8ce..fd89d9c67b3fc00c6c28916186351b8995d992a9 100644 (file)
@@ -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
 };
 
index 40b72d83d16aeb4ff1071f5c1a5321c383f7b5f8..2c1fb9b862e12d318e453698241f9d85cf944373 100644 (file)
@@ -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;
index 5ba5eb3851a690074b1cbebc85adc99035af63c1..a794cfe86b5f4272a6a0cb367532b17c82bfcf8d 100644 (file)
@@ -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;