Commits

Armin Rigo committed 9c6458c

Opaque structs and unions.

  • Participants
  • Parent commits b00cd52

Comments (0)

Files changed (3)

 
     def _get_struct_or_union_type(self, kind, type):
         key = '%s %s' % (kind, type.name)
-        assert key in self._declarations, "XXX opaque structs or unions"
-        fields = self._declarations[key].decls
-        fnames = [decl.name for decl in fields]
-        btypes = [self._get_btype(decl.type) for decl in fields]
+        if key in self._declarations:
+            fields = self._declarations[key].decls
+            fnames = tuple([decl.name for decl in fields])
+            btypes = tuple([self._get_btype(decl.type) for decl in fields])
+        else:   # opaque struct or union
+            fnames = None
+            btypes = None
         return self._backend.get_cached_btype(
-            'new_%s_type' % kind, type.name, tuple(fnames), tuple(btypes))
+            'new_%s_type' % kind, type.name, fnames, btypes)
 
 
 class FFILibrary(object):
         self.ffi = ffi
 
     def visit_FuncDecl(self, node):
-        self.ffi._declare('function ' + node.name, node)
+        self.ffi._declare('function ' + node.type.declname, node)
 
     def visit_Struct(self, node):
         if node.decls is not None:

File ffi/backend_ctypes.py

     def new_struct_type(self, name, fnames, BFieldTypes):
         #
         class struct(ctypes.Structure):
-            _fields_ = [(fname, BField._ctype)
-                        for (fname, BField) in zip(fnames, BFieldTypes)]
+            if fnames is not None:
+                _fields_ = [(fname, BField._ctype)
+                            for (fname, BField) in zip(fnames, BFieldTypes)]
         struct.__name__ = 'struct_%s' % name
         #
         class CTypesStruct(CTypesData):
             _reftypename = 'struct %s &' % name
 
             def __init__(self, init):
+                if fnames is None:
+                    raise TypeError("cannot instantiate opaque type %s" % (
+                        CTypesStruct,))
                 self._blob = struct()
                 if init is not None:
                     init = tuple(init)
                     for value, fname, BField in zip(init, fnames, BFieldTypes):
                         setattr(self._blob, fname, BField._to_ctypes(value))
         #
-        for fname, BField in zip(fnames, BFieldTypes):
-            if hasattr(CTypesStruct, fname):
-                raise ValueError("the field name %r conflicts in "
-                                 "the ctypes backend" % fname)
-            def getter(self, fname=fname, BField=BField):
-                return BField._from_ctypes(getattr(self._blob, fname))
-            def setter(self, value, fname=fname, BField=BField):
-                setattr(self._blob, fname, BField._to_ctypes(value))
-            setattr(CTypesStruct, fname, property(getter, setter))
+        if fnames is not None:
+            for fname, BField in zip(fnames, BFieldTypes):
+                if hasattr(CTypesStruct, fname):
+                    raise ValueError("the field name %r conflicts in "
+                                     "the ctypes backend" % fname)
+                def getter(self, fname=fname, BField=BField):
+                    return BField._from_ctypes(getattr(self._blob, fname))
+                def setter(self, value, fname=fname, BField=BField):
+                    setattr(self._blob, fname, BField._to_ctypes(value))
+                setattr(CTypesStruct, fname, property(getter, setter))
         #
         CTypesStruct._fix_class()
         return CTypesStruct
     def new_union_type(self, name, fnames, BFieldTypes):
         #
         class union(ctypes.Union):
-            _fields_ = [(fname, BField._ctype)
-                        for (fname, BField) in zip(fnames, BFieldTypes)]
+            if fnames is not None:
+                _fields_ = [(fname, BField._ctype)
+                            for (fname, BField) in zip(fnames, BFieldTypes)]
         union.__name__ = 'union_%s' % name
         #
         class CTypesUnion(CTypesData):
             _reftypename = 'union %s &' % name
 
             def __init__(self, init):
+                if fnames is None:
+                    raise TypeError("cannot instantiate opaque type %s" % (
+                        CTypesUnion,))
                 self._blob = union()
                 if init is not None:
                     fname = fnames[0]
                     BField = BFieldTypes[0]
                     setattr(self._blob, fname, BField._to_ctypes(init))
         #
-        for fname, BField in zip(fnames, BFieldTypes):
-            if hasattr(CTypesUnion, fname):
-                raise ValueError("the field name %r conflicts in "
-                                 "the ctypes backend" % fname)
-            def getter(self, fname=fname, BField=BField):
-                return BField._from_ctypes(getattr(self._blob, fname))
-            def setter(self, value, fname=fname, BField=BField):
-                setattr(self._blob, fname, BField._to_ctypes(value))
-            setattr(CTypesUnion, fname, property(getter, setter))
+        if fnames is not None:
+            for fname, BField in zip(fnames, BFieldTypes):
+                if hasattr(CTypesUnion, fname):
+                    raise ValueError("the field name %r conflicts in "
+                                     "the ctypes backend" % fname)
+                def getter(self, fname=fname, BField=BField):
+                    return BField._from_ctypes(getattr(self._blob, fname))
+                def setter(self, value, fname=fname, BField=BField):
+                    setattr(self._blob, fname, BField._to_ctypes(value))
+                setattr(CTypesUnion, fname, property(getter, setter))
         #
         CTypesUnion._fix_class()
         return CTypesUnion

File testing/backend_tests.py

         #
         py.test.raises(ValueError, ffi.new, "struct foo", [1, 2, 3, 4])
 
+    def test_struct_opaque(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "struct baz")
+        ffi.new("struct baz *")   # this works
+
     def test_union_simple(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("union foo { int a; short b, c; };")
         u = ffi.new("union foo", -2)
         assert u.a == -2
         py.test.raises((AttributeError, TypeError), "del u.a")
+
+    def test_union_opaque(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "union baz")
+        ffi.new("union baz *")   # this works