[pypy-commit] cffi default: hg merge add-float-dotdotdot
arigo
noreply at buildbot.pypy.org
Fri Aug 28 11:11:01 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r2264:6a79f6cdd2b8
Date: 2015-08-28 11:11 +0200
http://bitbucket.org/cffi/cffi/changeset/6a79f6cdd2b8/
Log: hg merge add-float-dotdotdot
diff --git a/c/realize_c_type.c b/c/realize_c_type.c
--- a/c/realize_c_type.c
+++ b/c/realize_c_type.c
@@ -166,6 +166,17 @@
"size (or not an integer type at all)");
return NULL;
}
+ else if (num == _CFFI__UNKNOWN_FLOAT_PRIM) {
+ PyErr_SetString(FFIError, "primitive floating-point type with an "
+ "unexpected size (or not a float type at all)");
+ return NULL;
+ }
+ else if (num == _CFFI__UNKNOWN_LONG_DOUBLE) {
+ PyErr_SetString(FFIError, "primitive floating-point type is "
+ "'long double', not supported for now with "
+ "the syntax 'typedef double... xxx;'");
+ return NULL;
+ }
else {
PyErr_Format(PyExc_NotImplementedError, "prim=%d", num);
return NULL;
diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -214,6 +214,12 @@
(size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \
_CFFI__UNKNOWN_PRIM)
+#define _cffi_prim_float(size) \
+ ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \
+ (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \
+ (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \
+ _CFFI__UNKNOWN_FLOAT_PRIM)
+
#define _cffi_check_int(got, got_nonpos, expected) \
((got_nonpos) == (expected <= 0) && \
(got) == (unsigned long long)expected)
diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py
--- a/cffi/cffi_opcode.py
+++ b/cffi/cffi_opcode.py
@@ -106,7 +106,9 @@
PRIM_UINTMAX = 47
_NUM_PRIM = 48
-_UNKNOWN_PRIM = -1
+_UNKNOWN_PRIM = -1
+_UNKNOWN_FLOAT_PRIM = -2
+_UNKNOWN_LONG_DOUBLE = -3
PRIMITIVE_TO_INDEX = {
'char': PRIM_CHAR,
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -648,10 +648,21 @@
assert typenames[-1] == '__dotdotdot__'
if len(typenames) == 1:
return model.unknown_type(decl.name)
- for t in typenames[:-1]:
- if t not in ['int', 'short', 'long', 'signed', 'unsigned', 'char']:
- raise api.FFIError(':%d: bad usage of "..."' % decl.coord.line)
+
+ if (typenames[:-1] == ['float'] or
+ typenames[:-1] == ['double']):
+ # not for 'long double' so far
+ result = model.UnknownFloatType(decl.name)
+ else:
+ for t in typenames[:-1]:
+ if t not in ['int', 'short', 'long', 'signed',
+ 'unsigned', 'char']:
+ raise api.FFIError(':%d: bad usage of "..."' %
+ decl.coord.line)
+ result = model.UnknownIntegerType(decl.name)
+
if self._uses_new_feature is None:
self._uses_new_feature = "'typedef %s... %s'" % (
' '.join(typenames[:-1]), decl.name)
- return model.UnknownIntegerType(decl.name)
+
+ return result
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -158,12 +158,23 @@
self.c_name_with_marker = name + '&'
def is_integer_type(self):
- return True # for now
+ return True
def build_backend_type(self, ffi, finishlist):
raise NotImplementedError("integer type '%s' can only be used after "
"compilation" % self.name)
+class UnknownFloatType(BasePrimitiveType):
+ _attrs_ = ('name', )
+
+ def __init__(self, name):
+ self.name = name
+ self.c_name_with_marker = name + '&'
+
+ def build_backend_type(self, ffi, finishlist):
+ raise NotImplementedError("float type '%s' can only be used after "
+ "compilation" % self.name)
+
class BaseFunctionType(BaseType):
_attrs_ = ('args', 'result', 'ellipsis')
diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h
--- a/cffi/parse_c_type.h
+++ b/cffi/parse_c_type.h
@@ -79,7 +79,9 @@
#define _CFFI_PRIM_UINTMAX 47
#define _CFFI__NUM_PRIM 48
-#define _CFFI__UNKNOWN_PRIM (-1)
+#define _CFFI__UNKNOWN_PRIM (-1)
+#define _CFFI__UNKNOWN_FLOAT_PRIM (-2)
+#define _CFFI__UNKNOWN_LONG_DOUBLE (-3)
struct _cffi_global_s {
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -468,6 +468,10 @@
if tp.is_integer_type() and tp.name != '_Bool':
converter = '_cffi_to_c_int'
extraarg = ', %s' % tp.name
+ elif isinstance(tp, model.UnknownFloatType):
+ # don't check with is_float_type(): it may be a 'long
+ # double' here, and _cffi_to_c_double would loose precision
+ converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
else:
converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
tp.name.replace(' ', '_'))
@@ -522,6 +526,8 @@
if isinstance(tp, model.BasePrimitiveType):
if tp.is_integer_type():
return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
+ elif isinstance(tp, model.UnknownFloatType):
+ return '_cffi_from_c_double(%s)' % (var,)
elif tp.name != 'long double':
return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
else:
@@ -1107,6 +1113,12 @@
' ) <= 0)' % (tp.name, tp.name, tp.name))
self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
+ def _emit_bytecode_UnknownFloatType(self, tp, index):
+ s = ('_cffi_prim_float(sizeof(%s) *\n'
+ ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
+ ' )' % (tp.name, tp.name))
+ self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
+
def _emit_bytecode_RawFunctionType(self, tp, index):
self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
index += 1
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -394,6 +394,16 @@
``(u)int(8,16,32,64)_t`` in Python, but in the generated C code,
only ``foo_t`` is used.
+* *New in version 1.3:* floating-point types: "``typedef
+ float... foo_t;``" (or equivalently "``typedef double... foo_t;``")
+ declares ``foo_t`` as a-float-or-a-double; the compiler will figure
+ out which it is. Note that if the actual C type is even larger
+ (``long double`` on some platforms), then compilation will fail.
+ The problem is that the Python "float" type cannot be used to store
+ the extra precision. (Use the non-dot-dot-dot syntax ``typedef long
+ double foo_t;`` as usual, which returns values that are not Python
+ floats at all but cdata "long double" objects.)
+
* unknown types: the syntax "``typedef ... foo_t;``" declares the type
``foo_t`` as opaque. Useful mainly for when the API takes and returns
``foo_t *`` without you needing to look inside the ``foo_t``. Also
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -3,8 +3,12 @@
======================
-v1.2.2
-======
+v1.3
+====
+
+* Pull request #64: out-of-line API mode: we can now declare
+ floating-point types with ``typedef float... foo_t;``. This only
+ works if ``foo_t`` is a float or a double, not ``long double``.
* Issue #217: fix possible unaligned pointer manipulation, which crash
on some architectures (64-bit, non-x86).
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1,6 +1,6 @@
import sys, os, py
-from cffi import FFI, VerificationError
+from cffi import FFI, VerificationError, FFIError
from cffi import recompiler
from testing.udir import udir
from testing.support import u
@@ -1056,14 +1056,54 @@
assert lib.nu == 20
def test_some_float_type():
- py.test.skip("later")
ffi = FFI()
- ffi.cdef("typedef double... foo_t; foo_t sum(foo_t[]);")
+ ffi.cdef("""
+ typedef double... foo_t;
+ typedef float... bar_t;
+ foo_t sum(foo_t[]);
+ bar_t neg(bar_t);
+ """)
lib = verify(ffi, 'test_some_float_type', """
typedef float foo_t;
static foo_t sum(foo_t x[]) { return x[0] + x[1]; }
+ typedef double bar_t;
+ static double neg(double x) { return -x; }
""")
assert lib.sum([40.0, 2.25]) == 42.25
+ assert lib.sum([12.3, 45.6]) != 12.3 + 45.6 # precision loss
+ assert lib.neg(12.3) == -12.3 # no precision loss
+ assert ffi.sizeof("foo_t") == ffi.sizeof("float")
+ assert ffi.sizeof("bar_t") == ffi.sizeof("double")
+
+def test_some_float_invalid_1():
+ ffi = FFI()
+ py.test.raises(FFIError, ffi.cdef, "typedef long double... foo_t;")
+
+def test_some_float_invalid_2():
+ ffi = FFI()
+ ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);")
+ lib = verify(ffi, 'test_some_float_invalid_2', """
+ typedef unsigned long foo_t;
+ foo_t neg(foo_t x) { return -x; }
+ """)
+ e = py.test.raises(ffi.error, getattr, lib, 'neg')
+ assert str(e.value) == ("primitive floating-point type with an unexpected "
+ "size (or not a float type at all)")
+
+def test_some_float_invalid_3():
+ ffi = FFI()
+ ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);")
+ lib = verify(ffi, 'test_some_float_invalid_3', """
+ typedef long double foo_t;
+ foo_t neg(foo_t x) { return -x; }
+ """)
+ if ffi.sizeof("long double") == ffi.sizeof("double"):
+ assert lib.neg(12.3) == -12.3
+ else:
+ e = py.test.raises(ffi.error, getattr, lib, 'neg')
+ assert str(e.value) == ("primitive floating-point type is "
+ "'long double', not supported for now with "
+ "the syntax 'typedef double... xxx;'")
def test_issue200():
ffi = FFI()
More information about the pypy-commit
mailing list