Skip to content

Commit f425509

Browse files
skirpichevdalcinlpicnixz
authored
gh-121249: unconditionally support complex types in struct (GH-132864)
Co-authored-by: Lisandro Dalcin <[email protected]> Co-authored-by: Bénédikt Tran <[email protected]>
1 parent e6c518d commit f425509

File tree

5 files changed

+21
-89
lines changed

5 files changed

+21
-89
lines changed

Doc/library/struct.rst

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -260,24 +260,17 @@ platform-dependent.
260260
+--------+--------------------------+--------------------+----------------+------------+
261261
| ``d`` | :c:expr:`double` | float | 8 | \(4) |
262262
+--------+--------------------------+--------------------+----------------+------------+
263+
| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) |
264+
+--------+--------------------------+--------------------+----------------+------------+
265+
| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) |
266+
+--------+--------------------------+--------------------+----------------+------------+
263267
| ``s`` | :c:expr:`char[]` | bytes | | \(9) |
264268
+--------+--------------------------+--------------------+----------------+------------+
265269
| ``p`` | :c:expr:`char[]` | bytes | | \(8) |
266270
+--------+--------------------------+--------------------+----------------+------------+
267271
| ``P`` | :c:expr:`void \*` | integer | | \(5) |
268272
+--------+--------------------------+--------------------+----------------+------------+
269273

270-
Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the
271-
C11 standard) is supported, the following format characters are available:
272-
273-
+--------+--------------------------+--------------------+----------------+------------+
274-
| Format | C Type | Python type | Standard size | Notes |
275-
+========+==========================+====================+================+============+
276-
| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) |
277-
+--------+--------------------------+--------------------+----------------+------------+
278-
| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) |
279-
+--------+--------------------------+--------------------+----------------+------------+
280-
281274
.. versionchanged:: 3.3
282275
Added support for the ``'n'`` and ``'N'`` formats.
283276

@@ -367,6 +360,11 @@ Notes:
367360
For the ``'E'`` and ``'C'`` format characters, the packed representation uses
368361
the IEEE 754 binary32 and binary64 format for components of the complex
369362
number, regardless of the floating-point format used by the platform.
363+
Note that complex types (``F`` and ``D``) are available unconditionally,
364+
despite complex types being an optional feature in C.
365+
As specified in the C11 standard, each complex type is represented by a
366+
two-element C array containing, respectively, the real and imaginary parts.
367+
370368

371369
A format character may be preceded by an integral repeat count. For example,
372370
the format string ``'4h'`` means exactly the same as ``'hhhh'``.

Doc/whatsnew/3.14.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,8 +1313,8 @@ struct
13131313
------
13141314

13151315
* Support the :c:expr:`float complex` and :c:expr:`double complex` C types in
1316-
the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``,
1317-
respectively) if the compiler has C11 complex arithmetic.
1316+
the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``
1317+
respectively).
13181318
(Contributed by Sergey B Kirpichev in :gh:`121249`.)
13191319

13201320

Lib/test/test_struct.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@
2222
INF = float('inf')
2323
NAN = float('nan')
2424

25-
try:
26-
struct.pack('D', 1j)
27-
have_c_complex = True
28-
except struct.error:
29-
have_c_complex = False
30-
3125
def iter_integer_formats(byteorders=byteorders):
3226
for code in integer_codes:
3327
for byteorder in byteorders:
@@ -796,7 +790,6 @@ def test_repr(self):
796790
s = struct.Struct('=i2H')
797791
self.assertEqual(repr(s), f'Struct({s.format!r})')
798792

799-
@unittest.skipUnless(have_c_complex, "requires C11 complex type support")
800793
def test_c_complex_round_trip(self):
801794
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
802795
-3, INF, -INF, NAN], 2)]
@@ -806,19 +799,6 @@ def test_c_complex_round_trip(self):
806799
round_trip = struct.unpack(f, struct.pack(f, z))[0]
807800
self.assertComplexesAreIdentical(z, round_trip)
808801

809-
@unittest.skipIf(have_c_complex, "requires no C11 complex type support")
810-
def test_c_complex_error(self):
811-
msg1 = "'F' format not supported on this system"
812-
msg2 = "'D' format not supported on this system"
813-
with self.assertRaisesRegex(struct.error, msg1):
814-
struct.pack('F', 1j)
815-
with self.assertRaisesRegex(struct.error, msg1):
816-
struct.unpack('F', b'1')
817-
with self.assertRaisesRegex(struct.error, msg2):
818-
struct.pack('D', 1j)
819-
with self.assertRaisesRegex(struct.error, msg2):
820-
struct.unpack('D', b'1')
821-
822802

823803
class UnpackIteratorTest(unittest.TestCase):
824804
"""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Always support the :c:expr:`float complex` and :c:expr:`double complex` C types in
2+
the :mod:`struct` module. Patch by Sergey B Kirpichev.

Modules/_struct.c

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
#include "pycore_long.h" // _PyLong_AsByteArray()
1313
#include "pycore_moduleobject.h" // _PyModule_GetState()
1414

15-
#ifdef Py_HAVE_C_COMPLEX
16-
# include "_complex.h" // complex
17-
#endif
1815
#include <stddef.h> // offsetof()
1916

2017
/*[clinic input]
@@ -495,25 +492,23 @@ nu_double(_structmodulestate *state, const char *p, const formatdef *f)
495492
return PyFloat_FromDouble(x);
496493
}
497494

498-
#ifdef Py_HAVE_C_COMPLEX
499495
static PyObject *
500496
nu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
501497
{
502-
float complex x;
498+
float x[2];
503499

504500
memcpy(&x, p, sizeof(x));
505-
return PyComplex_FromDoubles(creal(x), cimag(x));
501+
return PyComplex_FromDoubles(x[0], x[1]);
506502
}
507503

508504
static PyObject *
509505
nu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
510506
{
511-
double complex x;
507+
double x[2];
512508

513509
memcpy(&x, p, sizeof(x));
514-
return PyComplex_FromDoubles(creal(x), cimag(x));
510+
return PyComplex_FromDoubles(x[0], x[1]);
515511
}
516-
#endif
517512

518513
static PyObject *
519514
nu_void_p(_structmodulestate *state, const char *p, const formatdef *f)
@@ -788,13 +783,12 @@ np_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
788783
return 0;
789784
}
790785

791-
#ifdef Py_HAVE_C_COMPLEX
792786
static int
793787
np_float_complex(_structmodulestate *state, char *p, PyObject *v,
794788
const formatdef *f)
795789
{
796790
Py_complex c = PyComplex_AsCComplex(v);
797-
float complex x = CMPLXF((float)c.real, (float)c.imag);
791+
float x[2] = {(float)c.real, (float)c.imag};
798792

799793
if (c.real == -1 && PyErr_Occurred()) {
800794
PyErr_SetString(state->StructError,
@@ -810,7 +804,7 @@ np_double_complex(_structmodulestate *state, char *p, PyObject *v,
810804
const formatdef *f)
811805
{
812806
Py_complex c = PyComplex_AsCComplex(v);
813-
double complex x = CMPLX(c.real, c.imag);
807+
double x[2] = {c.real, c.imag};
814808

815809
if (c.real == -1 && PyErr_Occurred()) {
816810
PyErr_SetString(state->StructError,
@@ -820,25 +814,6 @@ np_double_complex(_structmodulestate *state, char *p, PyObject *v,
820814
memcpy(p, &x, sizeof(x));
821815
return 0;
822816
}
823-
#else
824-
static int
825-
np_complex_stub(_structmodulestate *state, char *p, PyObject *v,
826-
const formatdef *f)
827-
{
828-
PyErr_Format(state->StructError,
829-
"'%c' format not supported on this system",
830-
f->format);
831-
return -1;
832-
}
833-
static PyObject *
834-
nu_complex_stub(_structmodulestate *state, const char *p, const formatdef *f)
835-
{
836-
PyErr_Format(state->StructError,
837-
"'%c' format not supported on this system",
838-
f->format);
839-
return NULL;
840-
}
841-
#endif
842817

843818
static int
844819
np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -878,13 +853,8 @@ static const formatdef native_table[] = {
878853
{'e', sizeof(short), _Alignof(short), nu_halffloat, np_halffloat},
879854
{'f', sizeof(float), _Alignof(float), nu_float, np_float},
880855
{'d', sizeof(double), _Alignof(double), nu_double, np_double},
881-
#ifdef Py_HAVE_C_COMPLEX
882-
{'F', sizeof(float complex), _Alignof(float complex), nu_float_complex, np_float_complex},
883-
{'D', sizeof(double complex), _Alignof(double complex), nu_double_complex, np_double_complex},
884-
#else
885-
{'F', 1, 0, nu_complex_stub, np_complex_stub},
886-
{'D', 1, 0, nu_complex_stub, np_complex_stub},
887-
#endif
856+
{'F', 2*sizeof(float), _Alignof(float[2]), nu_float_complex, np_float_complex},
857+
{'D', 2*sizeof(double), _Alignof(double[2]), nu_double_complex, np_double_complex},
888858
{'P', sizeof(void *), _Alignof(void *), nu_void_p, np_void_p},
889859
{0}
890860
};
@@ -985,7 +955,6 @@ bu_double(_structmodulestate *state, const char *p, const formatdef *f)
985955
return unpack_double(p, 0);
986956
}
987957

988-
#ifdef Py_HAVE_C_COMPLEX
989958
static PyObject *
990959
bu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
991960
{
@@ -1015,7 +984,6 @@ bu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
1015984
}
1016985
return PyComplex_FromDoubles(x, y);
1017986
}
1018-
#endif
1019987

1020988
static PyObject *
1021989
bu_bool(_structmodulestate *state, const char *p, const formatdef *f)
@@ -1156,7 +1124,6 @@ bp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
11561124
return PyFloat_Pack8(x, p, 0);
11571125
}
11581126

1159-
#ifdef Py_HAVE_C_COMPLEX
11601127
static int
11611128
bp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
11621129
{
@@ -1186,7 +1153,6 @@ bp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd
11861153
}
11871154
return PyFloat_Pack8(x.imag, p + 8, 0);
11881155
}
1189-
#endif
11901156

11911157
static int
11921158
bp_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -1218,13 +1184,8 @@ static formatdef bigendian_table[] = {
12181184
{'e', 2, 0, bu_halffloat, bp_halffloat},
12191185
{'f', 4, 0, bu_float, bp_float},
12201186
{'d', 8, 0, bu_double, bp_double},
1221-
#ifdef Py_HAVE_C_COMPLEX
12221187
{'F', 8, 0, bu_float_complex, bp_float_complex},
12231188
{'D', 16, 0, bu_double_complex, bp_double_complex},
1224-
#else
1225-
{'F', 1, 0, nu_complex_stub, np_complex_stub},
1226-
{'D', 1, 0, nu_complex_stub, np_complex_stub},
1227-
#endif
12281189
{0}
12291190
};
12301191

@@ -1324,7 +1285,6 @@ lu_double(_structmodulestate *state, const char *p, const formatdef *f)
13241285
return unpack_double(p, 1);
13251286
}
13261287

1327-
#ifdef Py_HAVE_C_COMPLEX
13281288
static PyObject *
13291289
lu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
13301290
{
@@ -1354,7 +1314,6 @@ lu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
13541314
}
13551315
return PyComplex_FromDoubles(x, y);
13561316
}
1357-
#endif
13581317

13591318
static int
13601319
lp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -1489,7 +1448,6 @@ lp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
14891448
return PyFloat_Pack8(x, p, 1);
14901449
}
14911450

1492-
#ifdef Py_HAVE_C_COMPLEX
14931451
static int
14941452
lp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
14951453
{
@@ -1520,7 +1478,6 @@ lp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd
15201478
}
15211479
return PyFloat_Pack8(x.imag, p + 8, 1);
15221480
}
1523-
#endif
15241481

15251482
static formatdef lilendian_table[] = {
15261483
{'x', 1, 0, NULL},
@@ -1542,13 +1499,8 @@ static formatdef lilendian_table[] = {
15421499
{'e', 2, 0, lu_halffloat, lp_halffloat},
15431500
{'f', 4, 0, lu_float, lp_float},
15441501
{'d', 8, 0, lu_double, lp_double},
1545-
#ifdef Py_HAVE_C_COMPLEX
15461502
{'F', 8, 0, lu_float_complex, lp_float_complex},
15471503
{'D', 16, 0, lu_double_complex, lp_double_complex},
1548-
#else
1549-
{'F', 1, 0, nu_complex_stub, np_complex_stub},
1550-
{'D', 1, 0, nu_complex_stub, np_complex_stub},
1551-
#endif
15521504
{0}
15531505
};
15541506

0 commit comments

Comments
 (0)