(1)
The constructor accepts any object with a truth value.
+Additionally, if IEC 60559 compatible complex arithmetic (Annex G) is supported, the following
+complex types are available:
+
++----------------------------------+---------------------------------+-----------------+
+| ctypes type | C type | Python type |
++==================================+=================================+=================+
+| :class:`c_double_complex` | :c:expr:`double complex` | complex |
++----------------------------------+---------------------------------+-----------------+
+
+
All these types can be created by calling them with an optional initializer of
the correct type and value::
optional float initializer.
+.. class:: c_double_complex
+
+ Represents the C :c:expr:`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
if sizeof(c_longdouble) == sizeof(c_double):
c_longdouble = c_double
+try:
+ class c_double_complex(_SimpleCData):
+ _type_ = "C"
+except AttributeError:
+ pass
+
if _calcsize("l") == _calcsize("q"):
# if long and long long have the same size, make c_longlong an alias for c_long
c_longlong = c_long
+import ctypes
import math
import unittest
from ctypes import (CDLL, CFUNCTYPE, POINTER, create_string_buffer, sizeof,
self.assertEqual(lib.my_sqrt(4.0), 2.0)
self.assertEqual(lib.my_sqrt(2.0), math.sqrt(2.0))
+ @unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
+ "requires C11 complex type")
+ def test_csqrt(self):
+ lib.my_csqrt.argtypes = ctypes.c_double_complex,
+ lib.my_csqrt.restype = ctypes.c_double_complex
+ self.assertEqual(lib.my_csqrt(4), 2+0j)
+ self.assertAlmostEqual(lib.my_csqrt(-1+0.01j),
+ 0.004999937502734214+1.0000124996093955j)
+ self.assertAlmostEqual(lib.my_csqrt(-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
import array
+import ctypes
import struct
import sys
import unittest
+from itertools import combinations
+from math import copysign, isnan
from operator import truth
from ctypes import (byref, sizeof, alignment,
c_char, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
signed_ranges = valid_ranges(*signed_types)
bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]]
+class IntLike:
+ def __int__(self):
+ return 2
+
+class IndexLike:
+ def __index__(self):
+ return 2
+
+class FloatLike:
+ def __float__(self):
+ return 2.0
+
+class ComplexLike:
+ def __complex__(self):
+ return 1+1j
+
+
+INF = float("inf")
+NAN = float("nan")
+
class NumberTestCase(unittest.TestCase):
+ # from Lib/test/test_complex.py
+ def assertFloatsAreIdentical(self, x, y):
+ """assert that floats x and y are identical, in the sense that:
+ (1) both x and y are nans, or
+ (2) both x and y are infinities, with the same sign, or
+ (3) both x and y are zeros, with the same sign, or
+ (4) x and y are both finite and nonzero, and x == y
+
+ """
+ msg = 'floats {!r} and {!r} are not identical'
+
+ if isnan(x) or isnan(y):
+ if isnan(x) and isnan(y):
+ return
+ elif x == y:
+ if x != 0.0:
+ return
+ # both zero; check that signs match
+ elif copysign(1.0, x) == copysign(1.0, y):
+ return
+ else:
+ msg += ': zeros have different signs'
+ self.fail(msg.format(x, y))
+
+ def assertComplexesAreIdentical(self, x, y):
+ self.assertFloatsAreIdentical(x.real, y.real)
+ self.assertFloatsAreIdentical(x.imag, y.imag)
def test_default_init(self):
# default values are set to zero
def test_floats(self):
# c_float and c_double can be created from
# Python int and float
- class FloatLike:
- def __float__(self):
- return 2.0
f = FloatLike()
for t in float_types:
self.assertEqual(t(2.0).value, 2.0)
self.assertEqual(t(2).value, 2.0)
self.assertEqual(t(f).value, 2.0)
+ @unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
+ "requires C11 complex type")
+ def test_complex(self):
+ for t in [ctypes.c_double_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)
+ self.assertEqual(t(IndexLike()).value, 2+0j)
+ self.assertEqual(t(FloatLike()).value, 2+0j)
+ self.assertEqual(t(ComplexLike()).value, 1+1j)
+
+ @unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
+ "requires C11 complex type")
+ def test_complex_round_trip(self):
+ # Ensure complexes transformed exactly. The CMPLX macro should
+ # preserve special components (like inf/nan or signed zero).
+ 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)
+
def test_integers(self):
- class FloatLike:
- def __float__(self):
- return 2.0
f = FloatLike()
- class IntLike:
- def __int__(self):
- return 2
d = IntLike()
- class IndexLike:
- def __index__(self):
- return 2
i = IndexLike()
# integers cannot be constructed from floats,
# but from integer-like objects
MODULE_PYEXPAT_DEPS=@LIBEXPAT_INTERNAL@
MODULE_UNICODEDATA_DEPS=$(srcdir)/Modules/unicodedata_db.h $(srcdir)/Modules/unicodename_db.h
MODULE__BLAKE2_DEPS=$(srcdir)/Modules/_blake2/impl/blake2-config.h $(srcdir)/Modules/_blake2/impl/blake2-impl.h $(srcdir)/Modules/_blake2/impl/blake2.h $(srcdir)/Modules/_blake2/impl/blake2b-load-sse2.h $(srcdir)/Modules/_blake2/impl/blake2b-load-sse41.h $(srcdir)/Modules/_blake2/impl/blake2b-ref.c $(srcdir)/Modules/_blake2/impl/blake2b-round.h $(srcdir)/Modules/_blake2/impl/blake2b.c $(srcdir)/Modules/_blake2/impl/blake2s-load-sse2.h $(srcdir)/Modules/_blake2/impl/blake2s-load-sse41.h $(srcdir)/Modules/_blake2/impl/blake2s-load-xop.h $(srcdir)/Modules/_blake2/impl/blake2s-ref.c $(srcdir)/Modules/_blake2/impl/blake2s-round.h $(srcdir)/Modules/_blake2/impl/blake2s.c $(srcdir)/Modules/_blake2/blake2module.h $(srcdir)/Modules/hashlib.h
-MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h
+MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h $(srcdir)/Modules/_complex.h
MODULE__CTYPES_TEST_DEPS=$(srcdir)/Modules/_ctypes/_ctypes_test_generated.c.h
MODULE__CTYPES_MALLOC_CLOSURE=@MODULE__CTYPES_MALLOC_CLOSURE@
MODULE__DECIMAL_DEPS=$(srcdir)/Modules/_decimal/docstrings.h @LIBMPDEC_INTERNAL@
--- /dev/null
+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.
--- /dev/null
+/* Workarounds for buggy complex number arithmetic implementations. */
+
+#ifndef Py_HAVE_C_COMPLEX
+# error "this header file should only be included if Py_HAVE_C_COMPLEX is defined"
+#endif
+
+#include <complex.h>
+
+/* Other compilers (than clang), that claims to
+ implement C11 *and* define __STDC_IEC_559_COMPLEX__ - don't have
+ issue with CMPLX(). This is specific to glibc & clang combination:
+ https://sourceware.org/bugzilla/show_bug.cgi?id=26287
+
+ Here we fallback to using __builtin_complex(), available in clang
+ v12+. Else CMPLX implemented following C11 6.2.5p13: "Each complex type
+ has the same representation and alignment requirements as an array
+ type containing exactly two elements of the corresponding real type;
+ the first element is equal to the real part, and the second element
+ to the imaginary part, of the complex number.
+ */
+#if !defined(CMPLX)
+# if defined(__clang__) && __has_builtin(__builtin_complex)
+# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y))
+# else
+static inline double complex
+CMPLX(double real, double imag)
+{
+ double complex z;
+ ((double *)(&z))[0] = real;
+ ((double *)(&z))[1] = imag;
+ return z;
+}
+# endif
+#endif
[clinic start generated code]*/
/*[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";
+#else
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
+#endif
/*[clinic input]
_ctypes.c_wchar_p.from_param as c_wchar_p_from_param
goto error;
}
- stginfo->ffi_type_pointer = *fmt->pffi_type;
+ if (!fmt->pffi_type->elements) {
+ stginfo->ffi_type_pointer = *fmt->pffi_type;
+ }
+ else {
+ 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));
+ memcpy(stginfo->ffi_type_pointer.elements,
+ fmt->pffi_type->elements, 2 * sizeof(ffi_type));
+ }
stginfo->align = fmt->pffi_type->alignment;
stginfo->length = 0;
stginfo->size = fmt->pffi_type->size;
#include <Python.h>
+#include <ffi.h> // FFI_TARGET_HAS_COMPLEX_TYPE
+
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+# include "../_complex.h" // csqrt()
+# undef I // for _ctypes_test_generated.c.h
+#endif
#include <stdio.h> // printf()
#include <stdlib.h> // qsort()
#include <string.h> // memset()
return sqrt(a);
}
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+EXPORT(double complex) my_csqrt(double complex a)
+{
+ return csqrt(a);
+}
+#endif
+
EXPORT(void) my_qsort(void *base, size_t num, size_t width, int(*compare)(const void*, const void*))
{
qsort(base, num, width, compare);
#include "pycore_global_objects.h"// _Py_ID()
#include "pycore_traceback.h" // _PyTraceback_Add()
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+#include "../_complex.h" // complex
+#endif
+
#include "clinic/callproc.c.h"
#define CTYPES_CAPSULE_NAME_PYMEM "_ctypes pymem"
double d;
float f;
void *p;
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+ double complex C;
+#endif
};
struct argument {
#include <ffi.h>
#include "ctypes.h"
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+# include "../_complex.h" // complex
+#endif
#define CTYPES_CFIELD_CAPSULE_NAME_PYMEM "_ctypes/cfield.c pymem"
return PyFloat_FromDouble(val);
}
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+static PyObject *
+C_set(void *ptr, PyObject *value, Py_ssize_t size)
+{
+ Py_complex c = PyComplex_AsCComplex(value);
+
+ if (c.real == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ double complex x = CMPLX(c.real, c.imag);
+ memcpy(ptr, &x, sizeof(x));
+ _RET(value);
+}
+
+static PyObject *
+C_get(void *ptr, Py_ssize_t size)
+{
+ double complex x;
+
+ memcpy(&x, ptr, sizeof(x));
+ return PyComplex_FromDoubles(creal(x), cimag(x));
+}
+#endif
+
static PyObject *
d_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
{
{ 'B', B_set, B_get, NULL},
{ 'c', c_set, c_get, NULL},
{ '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},
+#endif
{ 'g', g_set, g_get, NULL},
{ 'f', f_set, f_get, NULL, f_set_sw, f_get_sw},
{ 'h', h_set, h_get, NULL, h_set_sw, h_get_sw},
case 'B': fd->pffi_type = &ffi_type_uchar; break;
case 'c': fd->pffi_type = &ffi_type_schar; break;
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;
+#endif
case 'g': fd->pffi_type = &ffi_type_longdouble; break;
case 'f': fd->pffi_type = &ffi_type_float; break;
case 'h': fd->pffi_type = &ffi_type_sshort; break;
# include <alloca.h>
#endif
+#include <ffi.h> // FFI_TARGET_HAS_COMPLEX_TYPE
+
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_typeobject.h" // _PyType_GetModuleState()
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+# include "../_complex.h" // complex
+#endif
+
#ifndef MS_WIN32
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
double d;
float f;
void *p;
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+ double complex C;
+#endif
} value;
PyObject *obj;
Py_ssize_t size; /* for the 'V' tag */
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="pyproject.props" />
+ <Import Project="libffi.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
long |
float |
double |
+ _Complex |
void |
struct |
|
(?: signed | unsigned ) # implies int
|
+ (?:
+ (?: (?: float | double | long\s+double ) \s+ )?
+ _Complex
+ )
+ |
+ (?:
+ _Complex
+ (?: \s+ (?: float | double | long\s+double ) )?
+ )
+ |
(?:
(?: (?: signed | unsigned ) \s+ )?
(?: (?: long | short ) \s+ )?
*) ;;
esac
+# check for _Complex C type
+#
+# Note that despite most compilers define __STDC_IEC_559_COMPLEX__ - almost
+# none properly support C11+ Annex G (where pure imaginary types
+# represented by _Imaginary are mandatory). This is a bug (see e.g.
+# llvm/llvm-project#60269), so we don't rely on presence
+# of __STDC_IEC_559_COMPLEX__.
+if test "$cross_compiling" = yes
+then :
+ ac_cv_c_complex_supported=no
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <complex.h>
+#define test(type, out) \
+{ \
+ type complex z = 1 + 2*I; z = z*z; \
+ (out) = (out) || creal(z) != -3 || cimag(z) != 4; \
+}
+int main(void)
+{
+ int res = 0;
+ test(float, res);
+ test(double, res);
+ test(long double, res);
+ return res;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ ac_cv_c_complex_supported=yes
+else $as_nop
+ ac_cv_c_complex_supported=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+if test "$ac_cv_c_complex_supported" = "yes"; then
+
+printf "%s\n" "#define Py_HAVE_C_COMPLEX 1" >>confdefs.h
+
+fi
+
# check for systems that require aligned memory access
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking aligned memory access is required" >&5
printf %s "checking aligned memory access is required... " >&6; }
if test "x$py_cv_module__ctypes_test" = xyes
then :
-
- as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_LDFLAGS=$LIBM$as_nl"
+ as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_CFLAGS=$LIBFFI_CFLAGS$as_nl"
+ as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_LDFLAGS=$LIBFFI_LIBS $LIBM$as_nl"
fi
if test "$py_cv_module__ctypes_test" = yes; then
*) ;;
esac
+# check for _Complex C type
+#
+# Note that despite most compilers define __STDC_IEC_559_COMPLEX__ - almost
+# none properly support C11+ Annex G (where pure imaginary types
+# represented by _Imaginary are mandatory). This is a bug (see e.g.
+# llvm/llvm-project#60269), so we don't rely on presence
+# of __STDC_IEC_559_COMPLEX__.
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <complex.h>
+#define test(type, out) \
+{ \
+ type complex z = 1 + 2*I; z = z*z; \
+ (out) = (out) || creal(z) != -3 || cimag(z) != 4; \
+}
+int main(void)
+{
+ int res = 0;
+ test(float, res);
+ test(double, res);
+ test(long double, res);
+ return res;
+}]])], [ac_cv_c_complex_supported=yes],
+[ac_cv_c_complex_supported=no],
+[ac_cv_c_complex_supported=no])
+if test "$ac_cv_c_complex_supported" = "yes"; then
+ AC_DEFINE([Py_HAVE_C_COMPLEX], [1],
+ [Defined if _Complex C type is available.])
+fi
+
# check for systems that require aligned memory access
AC_CACHE_CHECK([aligned memory access is required], [ac_cv_aligned_required],
[AC_RUN_IFELSE([AC_LANG_SOURCE([[
PY_STDLIB_MOD([_xxtestfuzz], [test "$TEST_MODULES" = yes])
PY_STDLIB_MOD([_ctypes_test],
[test "$TEST_MODULES" = yes], [test "$have_libffi" = yes -a "$ac_cv_func_dlopen" = yes],
- [], [$LIBM])
+ [$LIBFFI_CFLAGS], [$LIBFFI_LIBS $LIBM])
dnl Limited API template modules.
dnl Emscripten does not support shared libraries yet.
SipHash13: 3, externally defined: 0 */
#undef Py_HASH_ALGORITHM
+/* Defined if _Complex C type is available. */
+#undef Py_HAVE_C_COMPLEX
+
/* Define if year with century should be normalized for strftime. */
#undef Py_NORMALIZE_CENTURY