Skip to content

Commit

Permalink
Support for complex types with MSVC, called _Fcomplex and _Dcomplex (#57
Browse files Browse the repository at this point in the history
)
  • Loading branch information
arigo committed Feb 8, 2024
1 parent a5cf190 commit 4d660b1
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 32 deletions.
4 changes: 4 additions & 0 deletions src/c/_cffi_backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@
# if _MSC_VER < 1800 /* MSVC < 2013 */
typedef unsigned char _Bool;
# endif
# define _cffi_float_complex_t _Fcomplex /* include <complex.h> for it */
# define _cffi_double_complex_t _Dcomplex /* include <complex.h> for it */
#else
# include <stdint.h>
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
# include <alloca.h>
# endif
# define _cffi_float_complex_t float _Complex
# define _cffi_double_complex_t double _Complex
#endif

/* Convert from closure pointer to function pointer. */
Expand Down
2 changes: 2 additions & 0 deletions src/c/commontypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ static const char *common_simple_types[] = {
EQ("WINSTA", "HANDLE"),
EQ("WORD", "unsigned short"),
EQ("WPARAM", "UINT_PTR"),
EQ("_Dcomplex", "_cffi_double_complex_t"),
EQ("_Fcomplex", "_cffi_float_complex_t"),
#endif

EQ("bool", "_Bool"),
Expand Down
2 changes: 2 additions & 0 deletions src/c/parse_c_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,8 @@ int search_standard_typename(const char *p, size_t size)

case 'i':
if (size == 9 && !memcmp(p, "ptrdiff", 7)) return _CFFI_PRIM_PTRDIFF;
if (size == 21 && !memcmp(p, "_cffi_float_complex", 19)) return _CFFI_PRIM_FLOATCOMPLEX;
if (size == 22 && !memcmp(p, "_cffi_double_complex", 20)) return _CFFI_PRIM_DOUBLECOMPLEX;
break;

case 'l':
Expand Down
4 changes: 4 additions & 0 deletions src/cffi/_cffi_include.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,15 @@ extern "C" {
typedef unsigned char _Bool;
# endif
# endif
# define _cffi_float_complex_t _Fcomplex /* include <complex.h> for it */
# define _cffi_double_complex_t _Dcomplex /* include <complex.h> for it */
#else
# include <stdint.h>
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
# include <alloca.h>
# endif
# define _cffi_float_complex_t float _Complex
# define _cffi_double_complex_t double _Complex
#endif

#ifdef __GNUC__
Expand Down
4 changes: 2 additions & 2 deletions src/cffi/cffi_opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ def format_four_bytes(num):
'float': PRIM_FLOAT,
'double': PRIM_DOUBLE,
'long double': PRIM_LONGDOUBLE,
'float _Complex': PRIM_FLOATCOMPLEX,
'double _Complex': PRIM_DOUBLECOMPLEX,
'_cffi_float_complex_t': PRIM_FLOATCOMPLEX,
'_cffi_double_complex_t': PRIM_DOUBLECOMPLEX,
'_Bool': PRIM_BOOL,
'wchar_t': PRIM_WCHAR,
'char16_t': PRIM_CHAR16,
Expand Down
2 changes: 2 additions & 0 deletions src/cffi/commontypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE')
COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above
COMMON_TYPES['float _Complex'] = '_cffi_float_complex_t'
COMMON_TYPES['double _Complex'] = '_cffi_double_complex_t'

for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
if _type.endswith('_t'):
Expand Down
4 changes: 2 additions & 2 deletions src/cffi/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ class PrimitiveType(BasePrimitiveType):
'float': 'f',
'double': 'f',
'long double': 'f',
'float _Complex': 'j',
'double _Complex': 'j',
'_cffi_float_complex_t': 'j',
'_cffi_double_complex_t': 'j',
'_Bool': 'i',
# the following types are not primitive in the C sense
'wchar_t': 'c',
Expand Down
7 changes: 7 additions & 0 deletions src/cffi/vengine_cpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
if tp.is_integer_type() and tp.name != '_Bool':
converter = '_cffi_to_c_int'
extraarg = ', %s' % tp.name
elif tp.is_complex_type():
raise VerificationError(
"not implemented in verify(): complex types")
else:
converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
tp.name.replace(' ', '_'))
Expand Down Expand Up @@ -856,11 +859,15 @@ def _generate_setup_custom(self):
typedef unsigned char _Bool;
# endif
# endif
# define _cffi_float_complex_t _Fcomplex /* include <complex.h> for it */
# define _cffi_double_complex_t _Dcomplex /* include <complex.h> for it */
#else
# include <stdint.h>
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
# include <alloca.h>
# endif
# define _cffi_float_complex_t float _Complex
# define _cffi_double_complex_t double _Complex
#endif
#if PY_MAJOR_VERSION < 3
Expand Down
4 changes: 4 additions & 0 deletions src/cffi/vengine_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,10 +666,14 @@ def setter(library, value):
typedef unsigned char _Bool;
# endif
# endif
# define _cffi_float_complex_t _Fcomplex /* include <complex.h> for it */
# define _cffi_double_complex_t _Dcomplex /* include <complex.h> for it */
#else
# include <stdint.h>
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
# include <alloca.h>
# endif
# define _cffi_float_complex_t float _Complex
# define _cffi_double_complex_t double _Complex
#endif
'''
19 changes: 18 additions & 1 deletion testing/cffi0/test_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def test_primitive_category():
I = tp.is_integer_type()
assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t'))
assert F == (typename in ('float', 'double', 'long double'))
assert X == (typename in ('float _Complex', 'double _Complex'))
assert X == (typename in ('_cffi_float_complex_t', '_cffi_double_complex_t'))
assert I + F + C + X == 1 # one and only one of them is true

def test_all_integer_and_float_types():
Expand Down Expand Up @@ -280,6 +280,23 @@ def test_all_integer_and_float_types():
else:
assert foo(value) == value + 1

def test_all_complex_types():
pytest.skip("not implemented in verify(): complex types")
if sys.platform == 'win32':
typenames = ['_Fcomplex', '_Dcomplex']
header = '#include <complex.h>\n'
else:
typenames = ['float _Complex', 'double _Complex']
header = ''
#
ffi = FFI()
ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
for tp in typenames]))
e = pytest.raises(VerificationError, ffi.verify,
header + '\n'.join(["%s foo_%s(%s x) { return x; }" %
(tp, tp.replace(' ', '_'), tp)
for tp in typenames]))

def test_var_signed_integer_types():
ffi = FFI()
lst = all_signed_integer_types(ffi)
Expand Down
4 changes: 2 additions & 2 deletions testing/cffi1/test_new_ffi_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -1762,8 +1762,8 @@ def test_all_primitives(self):
"ptrdiff_t",
"size_t",
"ssize_t",
'float _Complex',
'double _Complex',
'_cffi_float_complex_t',
'_cffi_double_complex_t',
])
for name in PRIMITIVE_TO_INDEX:
x = ffi.sizeof(name)
Expand Down
4 changes: 3 additions & 1 deletion testing/cffi1/test_realize_c_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ def test_funcptr_rewrite_args():
check("int(*)(long[5])", "int(*)(long *)")

def test_all_primitives():
mapping = {"_cffi_float_complex_t": "float _Complex",
"_cffi_double_complex_t": "double _Complex"}
for name in cffi_opcode.PRIMITIVE_TO_INDEX:
check(name, name)
check(name, mapping.get(name, name))

def check_func(input, expected_output=None):
import _cffi_backend
Expand Down
81 changes: 58 additions & 23 deletions testing/cffi1/test_recompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2044,55 +2044,90 @@ def test_function_returns_partial_struct():
assert lib.f1(52).a == 52

def test_function_returns_float_complex():
if sys.platform == 'win32':
pytest.skip("MSVC may not support _Complex")
ffi = FFI()
ffi.cdef("float _Complex f1(float a, float b);");
lib = verify(ffi, "test_function_returns_float_complex", """
#include <complex.h>
static float _Complex f1(float a, float b) { return a + I*2.0f*b; }
""", no_cpp=True) # <complex.h> fails on some systems with C++
if sys.platform == 'win32':
lib = verify(ffi, "test_function_returns_float_complex", """
#include <complex.h>
static _Fcomplex f1(float a, float b) { return _FCbuild(a, 2.0f*b); }
""")
else:
lib = verify(ffi, "test_function_returns_float_complex", """
#include <complex.h>
static float _Complex f1(float a, float b) { return a + I*2.0f*b; }
""", no_cpp=True) # <complex.h> fails on some systems with C++
result = lib.f1(1.25, 5.1)
assert type(result) == complex
assert result.real == 1.25 # exact
assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact

def test_function_returns_double_complex():
if sys.platform == 'win32':
pytest.skip("MSVC may not support _Complex")
ffi = FFI()
ffi.cdef("double _Complex f1(double a, double b);");
lib = verify(ffi, "test_function_returns_double_complex", """
if sys.platform == 'win32':
lib = verify(ffi, "test_function_returns_double_complex", """
#include <complex.h>
static _Dcomplex f1(double a, double b) { return _Cbuild(a, 2.0*b); }
""")
else:
lib = verify(ffi, "test_function_returns_double_complex", """
#include <complex.h>
static double _Complex f1(double a, double b) { return a + I*2.0*b; }
""", no_cpp=True) # <complex.h> fails on some systems with C++
result = lib.f1(1.25, 5.1)
assert type(result) == complex
assert result.real == 1.25 # exact
assert result.imag == 2*5.1 # exact

def test_cdef_using_windows_complex():
if sys.platform != 'win32':
pytest.skip("only for MSVC")
ffi = FFI()
ffi.cdef("_Fcomplex f1(float a, float b); _Dcomplex f2(double a, double b);");
lib = verify(ffi, "test_cdef_using_windows_complex", """
#include <complex.h>
static double _Complex f1(double a, double b) { return a + I*2.0*b; }
""", no_cpp=True) # <complex.h> fails on some systems with C++
static _Fcomplex f1(float a, float b) { return _FCbuild(a, 2.0f*b); }
static _Dcomplex f2(double a, double b) { return _Cbuild(a, 2.0*b); }
""")
result = lib.f1(1.25, 5.1)
assert type(result) == complex
assert result.real == 1.25 # exact
assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact
result = lib.f2(1.25, 5.1)
assert type(result) == complex
assert result.real == 1.25 # exact
assert result.imag == 2*5.1 # exact

def test_function_argument_float_complex():
if sys.platform == 'win32':
pytest.skip("MSVC may not support _Complex")
ffi = FFI()
ffi.cdef("float f1(float _Complex x);");
lib = verify(ffi, "test_function_argument_float_complex", """
#include <complex.h>
static float f1(float _Complex x) { return cabsf(x); }
""", no_cpp=True) # <complex.h> fails on some systems with C++
if sys.platform == 'win32':
lib = verify(ffi, "test_function_argument_float_complex", """
#include <complex.h>
static float f1(_Fcomplex x) { return cabsf(x); }
""")
else:
lib = verify(ffi, "test_function_argument_float_complex", """
#include <complex.h>
static float f1(float _Complex x) { return cabsf(x); }
""", no_cpp=True) # <complex.h> fails on some systems with C++
x = complex(12.34, 56.78)
result = lib.f1(x)
assert abs(result - abs(x)) < 1e-5

def test_function_argument_double_complex():
if sys.platform == 'win32':
pytest.skip("MSVC may not support _Complex")
ffi = FFI()
ffi.cdef("double f1(double _Complex);");
lib = verify(ffi, "test_function_argument_double_complex", """
#include <complex.h>
static double f1(double _Complex x) { return cabs(x); }
""", no_cpp=True) # <complex.h> fails on some systems with C++
if sys.platform == 'win32':
lib = verify(ffi, "test_function_argument_double_complex", """
#include <complex.h>
static double f1(_Dcomplex x) { return cabs(x); }
""")
else:
lib = verify(ffi, "test_function_argument_double_complex", """
#include <complex.h>
static double f1(double _Complex x) { return cabs(x); }
""", no_cpp=True) # <complex.h> fails on some systems with C++
x = complex(12.34, 56.78)
result = lib.f1(x)
assert abs(result - abs(x)) < 1e-11
Expand Down
23 changes: 22 additions & 1 deletion testing/cffi1/test_verify1.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def test_primitive_category():
I = tp.is_integer_type()
assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t'))
assert F == (typename in ('float', 'double', 'long double'))
assert X == (typename in ('float _Complex', 'double _Complex'))
assert X == (typename in ('_cffi_float_complex_t', '_cffi_double_complex_t'))
assert I + F + C + X == 1 # one and only one of them is true

def test_all_integer_and_float_types():
Expand Down Expand Up @@ -254,6 +254,27 @@ def test_all_integer_and_float_types():
else:
assert foo(value) == value + 1

def test_all_complex_types():
if sys.platform == 'win32':
typenames = ['_Fcomplex', '_Dcomplex']
header = '#include <complex.h>\n'
else:
typenames = ['float _Complex', 'double _Complex']
header = ''
#
ffi = FFI()
ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
for tp in typenames]))
lib = ffi.verify(
header + '\n'.join(["%s foo_%s(%s x) { return x; }" %
(tp, tp.replace(' ', '_'), tp)
for tp in typenames]))
for typename in typenames:
foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_'))
assert foo(42 + 1j) == 42 + 1j
assert foo(ffi.cast(typename, 46 - 3j)) == 46 - 3j
pytest.raises(TypeError, foo, ffi.NULL)

def test_var_signed_integer_types():
ffi = FFI()
lst = all_signed_integer_types(ffi)
Expand Down

0 comments on commit 4d660b1

Please sign in to comment.