[pypy-commit] cffi default: Issue 75: implement multidimensional use of '[...]'.

arigo noreply at buildbot.pypy.org
Sat May 30 11:17:29 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2129:d2e7bb656d74
Date: 2015-05-30 11:17 +0200
http://bitbucket.org/cffi/cffi/changeset/d2e7bb656d74/

Log:	Issue 75: implement multidimensional use of '[...]'.

diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -338,7 +338,9 @@
             else:
                 length = self._parse_constant(
                     typenode.dim, partial_length_ok=partial_length_ok)
-            return model.ArrayType(self._get_type(typenode.type), length)
+            tp = self._get_type(typenode.type,
+                                partial_length_ok=(length == '...'))
+            return model.ArrayType(tp, length)
         #
         if isinstance(typenode, pycparser.c_ast.PtrDecl):
             # pointer type
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -753,7 +753,9 @@
             ptr_struct_name = tp_struct.get_c_name('*')
             actual_length = '_cffi_array_len(((%s)0)->%s)' % (
                 ptr_struct_name, field_name)
-            tp_field = tp_field.resolve_length(actual_length)
+            tp_item = self._field_type(tp_struct, '%s[0]' % field_name,
+                                       tp_field.item)
+            tp_field = model.ArrayType(tp_item, actual_length)
         return tp_field
 
     def _struct_collecttype(self, tp):
@@ -775,16 +777,16 @@
                     and ftype.is_integer_type()) or fbitsize >= 0:
                     # accept all integers, but complain on float or double
                     prnt('  (void)((p->%s) << 1);' % fname)
-                elif (isinstance(ftype, model.ArrayType)
-                      and (ftype.length is None or ftype.length == '...')):
-                    # for C++: "int(*)tmp[] = &p->a;" errors out if p->a is
-                    # declared as "int[5]".  Instead, write "int *tmp = p->a;".
-                    prnt('  { %s = p->%s; (void)tmp; }' % (
-                        ftype.item.get_c_name('*tmp', 'field %r'%fname), fname))
-                else:
-                    # only accept exactly the type declared.
-                    prnt('  { %s = &p->%s; (void)tmp; }' % (
-                        ftype.get_c_name('*tmp', 'field %r'%fname), fname))
+                    continue
+                # only accept exactly the type declared, except that '[]'
+                # is interpreted as a '*' and so will match any array length.
+                # (It would also match '*', but that's harder to detect...)
+                while (isinstance(ftype, model.ArrayType)
+                       and (ftype.length is None or ftype.length == '...')):
+                    ftype = ftype.item
+                    fname = fname + '[0]'
+                prnt('  { %s = &p->%s; (void)tmp; }' % (
+                    ftype.get_c_name('*tmp', 'field %r'%fname), fname))
             except ffiplatform.VerificationError as e:
                 prnt('  /* %s */' % str(e))   # cannot verify it, ignore
         prnt('}')
@@ -1056,7 +1058,8 @@
     def _global_type(self, tp, global_name):
         if isinstance(tp, model.ArrayType) and tp.length == '...':
             actual_length = '_cffi_array_len(%s)' % (global_name,)
-            tp = tp.resolve_length(actual_length)
+            tp_item = self._global_type(tp.item, '%s[0]' % global_name)
+            tp = model.ArrayType(tp_item, actual_length)
         return tp
 
     def _generate_cpy_variable_collecttype(self, tp, name):
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -387,11 +387,11 @@
 
 *  array lengths: when used as structure fields or in global variables,
    arrays can have an unspecified length, as in "``int n[...];``".  The
-   length is completed by the C compiler.  (Only the outermost array
-   may have an unknown length, in case of array-of-array.)
+   length is completed by the C compiler.
    This is slightly different from "``int n[];``", because the latter
    means that the length is not known even to the C compiler, and thus
-   no attempt is made to complete it.
+   no attempt is made to complete it.  *New in version 1.0.4:* support
+   for multidimensional arrays: "``int n[...][...];``".
 
 *  enums: if you don't know the exact order (or values) of the declared
    constants, then use this syntax: "``enum foo { A, B, C, ... };``"
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -6,6 +6,10 @@
 1.0.4
 =====
 
+* Out-of-line API mode: we can now declare multidimensional arrays
+  (as fields or as globals) with ``int n[...][...]``.  Before, only the
+  outermost dimension would support the ``...`` syntax.
+
 * Issue #175: in ABI mode: we now support any constant declaration,
   instead of only integers whose value is given in the cdef.  Such "new"
   constants, i.e. either non-integers or without a value given in the
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
@@ -907,3 +907,28 @@
     """)
     assert lib.getx(lib.myglob) == 42.5
     assert lib.getx(lib.increment(lib.myglob)) == 43.5
+
+def test_struct_array_guess_length_2():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[...][...]; };")
+    lib = verify(ffi, 'test_struct_array_guess_length_2',
+                 "struct foo_s { int x; int a[5][8]; int y; };")
+    assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int')
+    assert s.a[4][7] == 0
+    py.test.raises(IndexError, 's.a[4][8]')
+    py.test.raises(IndexError, 's.a[5][0]')
+    assert ffi.typeof(s.a) == ffi.typeof("int[5][8]")
+    assert ffi.typeof(s.a[0]) == ffi.typeof("int[8]")
+
+def test_global_var_array_2():
+    ffi = FFI()
+    ffi.cdef("int a[...][...];")
+    lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];')
+    lib.a[9][7] = 123456
+    assert lib.a[9][7] == 123456
+    py.test.raises(IndexError, 'lib.a[0][8]')
+    py.test.raises(IndexError, 'lib.a[10][0]')
+    assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]")
+    assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]")


More information about the pypy-commit mailing list