mirror of https://github.com/python/cpython
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 |
|
| 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_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
|
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
|
.. 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
|
.. class:: c_int
|
||||||
|
|
||||||
Represents the C :c:expr:`signed int` datatype. The constructor accepts an
|
Represents the C :c:expr:`signed int` datatype. The constructor accepts an
|
||||||
|
|
|
@ -208,6 +208,10 @@ if sizeof(c_longdouble) == sizeof(c_double):
|
||||||
try:
|
try:
|
||||||
class c_double_complex(_SimpleCData):
|
class c_double_complex(_SimpleCData):
|
||||||
_type_ = "C"
|
_type_ = "C"
|
||||||
|
class c_float_complex(_SimpleCData):
|
||||||
|
_type_ = "E"
|
||||||
|
class c_longdouble_complex(_SimpleCData):
|
||||||
|
_type_ = "F"
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,20 @@ class LibTest(unittest.TestCase):
|
||||||
self.assertAlmostEqual(lib.my_csqrt(-1-0.01j),
|
self.assertAlmostEqual(lib.my_csqrt(-1-0.01j),
|
||||||
0.004999937502734214-1.0000124996093955j)
|
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):
|
def test_qsort(self):
|
||||||
comparefunc = CFUNCTYPE(c_int, POINTER(c_char), POINTER(c_char))
|
comparefunc = CFUNCTYPE(c_int, POINTER(c_char), POINTER(c_char))
|
||||||
lib.my_qsort.argtypes = c_void_p, c_size_t, c_size_t, comparefunc
|
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"),
|
@unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
|
||||||
"requires C11 complex type")
|
"requires C11 complex type")
|
||||||
def test_complex(self):
|
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).value, 1+0j)
|
||||||
self.assertEqual(t(1.0).value, 1+0j)
|
self.assertEqual(t(1.0).value, 1+0j)
|
||||||
self.assertEqual(t(1+0.125j).value, 1+0.125j)
|
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,
|
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
|
||||||
-3, INF, -INF, NAN], 2)]
|
-3, INF, -INF, NAN], 2)]
|
||||||
for z in values:
|
for z in values:
|
||||||
with self.subTest(z=z):
|
for t in [ctypes.c_double_complex, ctypes.c_float_complex,
|
||||||
z2 = ctypes.c_double_complex(z).value
|
ctypes.c_longdouble_complex]:
|
||||||
self.assertComplexesAreIdentical(z, z2)
|
with self.subTest(z=z, type=t):
|
||||||
|
self.assertComplexesAreIdentical(z, t(z).value)
|
||||||
|
|
||||||
def test_integers(self):
|
def test_integers(self):
|
||||||
f = FloatLike()
|
f = FloatLike()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
Support :c:expr:`double complex` C type in :mod:`ctypes` via
|
Support :c:expr:`float complex`, :c:expr:`double complex` and
|
||||||
:class:`~ctypes.c_double_complex` if compiler has C11 complex
|
:c:expr:`long double complex` C types in :mod:`ctypes` as
|
||||||
arithmetic. Patch by Sergey B Kirpichev.
|
: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(CMPLX)
|
||||||
# if defined(__clang__) && __has_builtin(__builtin_complex)
|
# if defined(__clang__) && __has_builtin(__builtin_complex)
|
||||||
# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y))
|
# 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
|
# else
|
||||||
static inline double complex
|
static inline double complex
|
||||||
CMPLX(double real, double imag)
|
CMPLX(double real, double imag)
|
||||||
|
@ -30,5 +32,23 @@ CMPLX(double real, double imag)
|
||||||
((double *)(&z))[1] = imag;
|
((double *)(&z))[1] = imag;
|
||||||
return z;
|
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
|
||||||
#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]*/
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=dd4d9646c56f43a9]*/
|
||||||
|
|
||||||
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
#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
|
#else
|
||||||
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
|
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
|
||||||
#endif
|
#endif
|
||||||
|
@ -2234,12 +2234,13 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
stginfo->ffi_type_pointer = *fmt->pffi_type;
|
stginfo->ffi_type_pointer = *fmt->pffi_type;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
const size_t els_size = sizeof(fmt->pffi_type->elements);
|
||||||
stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
|
stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
|
||||||
stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
|
stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
|
||||||
stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
|
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,
|
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->align = fmt->pffi_type->alignment;
|
||||||
stginfo->length = 0;
|
stginfo->length = 0;
|
||||||
|
|
|
@ -454,6 +454,16 @@ EXPORT(double complex) my_csqrt(double complex a)
|
||||||
{
|
{
|
||||||
return csqrt(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
|
#endif
|
||||||
|
|
||||||
EXPORT(void) my_qsort(void *base, size_t num, size_t width, int(*compare)(const void*, const void*))
|
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;
|
void *p;
|
||||||
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
||||||
double complex C;
|
double complex C;
|
||||||
|
float complex E;
|
||||||
|
long double complex F;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1112,6 +1112,50 @@ C_get(void *ptr, Py_ssize_t size)
|
||||||
memcpy(&x, ptr, sizeof(x));
|
memcpy(&x, ptr, sizeof(x));
|
||||||
return PyComplex_FromDoubles(creal(x), cimag(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
|
#endif
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1621,6 +1665,8 @@ static struct fielddesc formattable[] = {
|
||||||
{ 'd', d_set, d_get, NULL, d_set_sw, d_get_sw},
|
{ 'd', d_set, d_get, NULL, d_set_sw, d_get_sw},
|
||||||
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
||||||
{ 'C', C_set, C_get, NULL},
|
{ 'C', C_set, C_get, NULL},
|
||||||
|
{ 'E', E_set, E_get, NULL},
|
||||||
|
{ 'F', F_set, F_get, NULL},
|
||||||
#endif
|
#endif
|
||||||
{ 'g', g_set, g_get, NULL},
|
{ 'g', g_set, g_get, NULL},
|
||||||
{ 'f', f_set, f_get, NULL, f_set_sw, f_get_sw},
|
{ '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;
|
case 'd': fd->pffi_type = &ffi_type_double; break;
|
||||||
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
||||||
case 'C': fd->pffi_type = &ffi_type_complex_double; break;
|
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
|
#endif
|
||||||
case 'g': fd->pffi_type = &ffi_type_longdouble; break;
|
case 'g': fd->pffi_type = &ffi_type_longdouble; break;
|
||||||
case 'f': fd->pffi_type = &ffi_type_float; break;
|
case 'f': fd->pffi_type = &ffi_type_float; break;
|
||||||
|
|
|
@ -401,6 +401,8 @@ struct tagPyCArgObject {
|
||||||
void *p;
|
void *p;
|
||||||
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
|
||||||
double complex C;
|
double complex C;
|
||||||
|
float complex E;
|
||||||
|
long double complex F;
|
||||||
#endif
|
#endif
|
||||||
} value;
|
} value;
|
||||||
PyObject *obj;
|
PyObject *obj;
|
||||||
|
|
Loading…
Reference in New Issue