8000 gh-61103: Support float and long double complex types in ctypes modul… · python/cpython@51c4a32 · GitHub
[go: up one dir, main page]

Skip to content

Commit 51c4a32

Browse files
skirpichevvstinnerpicnixz
authored
gh-61103: Support float and long double complex types in ctypes module (#121248)
This amends 6988ff0: 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>
1 parent c9bdfbe commit 51c4a32

File tree

11 files changed

+135
-10
lines changed

11 files changed

+135
-10
lines changed

Doc/library/ctypes.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,12 @@ complex types are available:
272272
+----------------------------------+---------------------------------+-----------------+
273273
| ctypes type | C type | Python type |
274274
+==================================+=================================+=================+
275+
| :class:`c_float_complex` | :c:expr:`float complex` | complex |
276+
+----------------------------------+---------------------------------+-----------------+
275277
| :class:`c_double_complex` | :c:expr:`double complex` | complex |
276278
+----------------------------------+---------------------------------+-----------------+
279+
| :class:`c_longdouble_complex` | :c:expr:`long double complex` | complex |
280+
+----------------------------------+---------------------------------+-----------------+
277281

278282

279283
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:
23022306
.. versionadded:: 3.14
23032307

23042308

2309+
.. class:: c_float_complex
2310+
2311+
Represents the C :c:expr:`float complex` datatype, if available. The
2312+
constructor accepts an optional :class:`complex` initializer.
2313+
2314+
.. versionadded:: 3.14
2315+
2316+
2317+
.. class:: c_longdouble_complex
2318+
2319+
Represents the C :c:expr:`long double complex` datatype, if available. The
2320+
constructor accepts an optional :class:`complex` initializer.
2321+
2322+
.. versionadded:: 3.14
2323+
2324+
23052325
.. class:: c_int
23062326

23072327
Represents the C :c:expr:`signed int` datatype. The constructor accepts an

Lib/ctypes/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ class c_longdouble(_SimpleCData):
208208
try:
209209
class c_double_complex(_SimpleCData):
210210
_type_ = "C"
211+
class c_float_complex(_SimpleCData):
212+
_type_ = "E"
213+
class c_longdouble_complex(_SimpleCData):
214+
_type_ = "F"
211215
except AttributeError:
212216
pass
213217

Lib/test/test_ctypes/test_libc.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ def test_csqrt(self):
3333
self.assertAlmostEqual(lib.my_csqrt(-1-0.01j),
3434
0.004999937502734214-1.0000124996093955j)
3535

36+
lib.my_csqrtf.argtypes = ctypes.c_float_complex,
37+
lib.my_csqrtf.restype = ctypes.c_float_complex
38+
self.assertAlmostEqual(lib.my_csqrtf(-1+0.01j),
39+
0.004999937502734214+1.0000124996093955j)
40+
self.assertAlmostEqual(lib.my_csqrtf(-1-0.01j),
41+
0.004999937502734214-1.0000124996093955j)
42+
43+
lib.my_csqrtl.argtypes = ctypes.c_longdouble_complex,
44+
lib.my_csqrtl.restype = ctypes.c_longdouble_complex
45+
self.assertAlmostEqual(lib.my_csqrtl(-1+0.01j),
46+
0.004999937502734214+1.0000124996093955j)
47+
self.assertAlmostEqual(lib.my_csqrtl(-1-0.01j),
48+
0.004999937502734214-1.0000124996093955j)
49+
3650
def test_qsort(self):
3751
comparefunc = CFUNCTYPE(c_int, POINTER(c_char), POINTER(c_char))
3852
lib.my_qsort.argtypes = c_void_p, c_size_t, c_size_t, comparefunc

Lib/test/test_ctypes/test_numbers.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ def test_floats(self):
146146
@unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
147147
"requires C11 complex type")
148148
def test_complex(self):
149-
for t in [ctypes.c_double_complex]:
149+
for t in [ctypes.c_double_complex, ctypes.c_float_complex,
150+
ctypes.c_longdouble_complex]:
150151
self.assertEqual(t(1).value, 1+0j)
151152
self.assertEqual(t(1.0).value, 1+0j)
152153
self.assertEqual(t(1+0.125j).value, 1+0.125j)
@@ -162,9 +163,10 @@ def test_complex_round_trip(self):
162163
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
163164
-3, INF, -INF, NAN], 2)]
164165
for z in values:
165-
with self.subTest(z=z):
166-
z2 = ctypes.c_double_complex(z).value
167-
self.assertComplexesAreIdentical(z, z2)
166+
for t in [ctypes.c_double_complex, ctypes.c_float_complex,
167+
ctypes.c_longdouble_complex]:
168+
with self.subTest(z=z, type=t):
169+
self.assertComplexesAreIdentical(z, t(z).value)
168170

169171
def test_integers(self):
170172
f = FloatLike()
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
Support :c:expr:`double complex` C type in :mod:`ctypes` via
2-
:class:`~ctypes.c_double_complex` if compiler has C11 complex
3-
arithmetic. Patch by Sergey B Kirpichev.
1+
Support :c:expr:`float complex`, :c:expr:`double complex` and
2+
:c:expr:`long double complex` C types in :mod:`ctypes` as
3+
:class:`~ctypes.c_float_complex`, :class:`~ctypes.c_double_complex` and
4+
:class:`~ctypes.c_longdouble_complex` if the compiler has C11 complex arithmetic.
5+
Patch by Sergey B Kirpichev.

Modules/_complex.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#if !defined(CMPLX)
2222
# if defined(__clang__) && __has_builtin(__builtin_complex)
2323
# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y))
24+
# define CMPLXF(x, y) __builtin_complex ((float) (x), (float) (y))
25+
# define CMPLXL(x, y) __builtin_complex ((long double) (x), (long double) (y))
2426
# else
2527
static inline double complex
2628
CMPLX(double real, double imag)
@@ -30,5 +32,23 @@ CMPLX(double real, double imag)
3032
((double *)(&z))[1] = imag;
3133
return z;
3234
}
35+
36+
static inline float complex
37+
CMPLXF(float real, float imag)
38+
{
39+
float complex z;
40+
((float *)(&z))[0] = real;
41+
((float *)(&z))[1] = imag;
42+
return z;
43+
}
44+
45+
static inline long double complex
46+
CMPLXL(long double real, long double imag)
47+
{
48+
long double complex z;
49+
((long double *)(&z))[0] = real;
50+
((long double *)(&z))[1] = imag;
51+
return z;
52+
}
3353
# endif
3454
#endif

Modules/_ctypes/_ctypes.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,7 +1751,7 @@ class _ctypes.c_void_p "PyObject *" "clinic_state_sub()->PyCSimpleType_Type"
17511751
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=dd4d9646c56f43a9]*/
17521752

17531753
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
1754-
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCfuzZqQPXOv?g";
1754+
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCEFfuzZqQPXOv?g";
17551755
#else
17561756
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
17571757
#endif
@@ -2234,12 +2234,13 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
22342234
stginfo->ffi_type_pointer = *fmt->pffi_type;
22352235
}
22362236
else {
2237+
const size_t els_size = sizeof(fmt->pffi_type->elements);
22372238
stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
22382239
stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
22392240
stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
2240-
stginfo->ffi_type_pointer.elements = PyMem_Malloc(2 * sizeof(ffi_type));
2241+
stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size);
22412242
memcpy(stginfo->ffi_type_pointer.elements,
2242-
fmt->pffi_type->elements, 2 * sizeof(ffi_type));
2243+
fmt->pffi_type->elements, els_size);
22432244
}
22442245
stginfo->align = fmt->pffi_type->alignment;
22452246
stginfo->length = 0;

Modules/_ctypes/_ctypes_test.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,16 @@ EXPORT(double complex) my_csqrt(double complex a)
454454
{
455455
return csqrt(a);
456456
}
457+
458+
EXPORT(float complex) my_csqrtf(float complex a)
459+
{
460+
return csqrtf(a);
461+
}
462+
463+
EXPORT(long double complex) my_csqrtl(long double complex a)
464+
{
465+
return csqrtl(a);
466+
}
457467
#endif
458468

459469
EXPORT(void) my_qsort(void *base, size_t num, size_t width, int(*compare)(const void*, const void*))

Modules/_ctypes/callproc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,8 @@ union result {
657657
void *p;
658658
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
659659
double complex C;
660+
float complex E;
661+
long double complex F;
660662
#endif
661663
};
662664

Modules/_ctypes/cfield.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,50 @@ C_get(void *ptr, Py_ssize_t size)
11121112
memcpy(&x, ptr, sizeof(x));
11131113
return PyComplex_FromDoubles(creal(x), cimag(x));
11141114
}
1115+
1116+
static PyObject *
1117+
E_set(void *ptr, PyObject *value, Py_ssize_t size)
1118+
{
1119+
Py_complex c = PyComplex_AsCComplex(value);
1120+
1121+
if (c.real == -1 && PyErr_Occurred()) {
1122+
return NULL;
1123+
}
1124+
float complex x = CMPLXF((float)c.real, (float)c.imag);
1125+
memcpy(ptr, &x, sizeof(x));
1126+
_RET(value);
1127+
}
1128+
1129+
static PyObject *
1130+
E_get(void *ptr, Py_ssize_t size)
1131+
{
1132+
float complex x;
1133+
1134+
memcpy(&x, ptr, sizeof(x));
1135+
return PyComplex_FromDoubles(crealf(x), cimagf(x));
1136+
}
1137+
1138+
static PyObject *
1139+
F_set(void *ptr, PyObject *value, Py_ssize_t size)
1140+
{
1141+
Py_complex c = PyComplex_AsCComplex(value);
1142+
10000
1143+
if (c.real == -1 && PyErr_Occurred()) {
1144+
return NULL;
1145+
}
1146+
long double complex x = CMPLXL(c.real, c.imag);
1147+
memcpy(ptr, &x, sizeof(x));
1148+
_RET(value);
1149+
}
1150+
1151+
static PyObject *
1152+
F_get(void *ptr, Py_ssize_t size)
1153+
{
1154+
long double complex x;
1155+
1156+
memcpy(&x, ptr, sizeof(x));
1157+
return PyComplex_FromDoubles((double)creall(x), (double)cimagl(x));
1158+
}
11151159
#endif
11161160

11171161
static PyObject *
@@ -1621,6 +1665,8 @@ static struct fielddesc formattable[] = {
16211665
{ 'd', d_set, d_get, NULL, d_set_sw, d_get_sw},
16221666
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
16231667
{ 'C', C_set, C_get, NULL},
1668+
{ 'E', E_set, E_get, NULL},
1669+
{ 'F', F_set, F_get, NULL},
16241670
#endif
16251671
{ 'g', g_set, g_get, NULL},
16261672
{ 'f', f_set, f_get, NULL, f_set_sw, f_get_sw},
@@ -1674,6 +1720,8 @@ _ctypes_init_fielddesc(void)
16741720
case 'd': fd->pffi_type = &ffi_type_double; break;
16751721
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
16761722
case 'C': fd->pffi_type = &ffi_type_complex_double; break;
1723+
case 'E': fd->pffi_type = &ffi_type_complex_float; break;
1724+
case 'F': fd->pffi_type = &ffi_type_complex_longdouble; break;
16771725
#endif
16781726
case 'g': fd->pffi_type = &ffi_type_longdouble; break;
16791727
case 'f': fd->pffi_type = &ffi_type_float; break;

Modules/_ctypes/ctypes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ struct tagPyCArgObject {
401401
void *p;
402402
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
403403
double complex C;
404+
float complex E;
405+
long double complex F;
404406
#endif
405407
} value;
406408
PyObject *obj;

0 commit comments

Comments
 (0)
0