Armin Rigo avatar Armin Rigo committed 53f43a6

Unions, a copy-paste of Structs. Is it worth trying to share some
code? They have subtle differences here and there...

Comments (0)

Files changed (3)

         self._backend = backend
         self._functions = {}
         self._structs = {}
+        self._unions = {}
         self._cached_btypes = {}
         self._cached_parsed_types = {}
         self.C = FFILibrary(self, self._backend.load_library())
                 btypes = [self._get_btype(decl.type) for decl in fields]
                 return self._backend.get_cached_btype(
                     'new_struct_type', type.name, tuple(fnames), tuple(btypes))
+            #
+            if isinstance(type, pycparser.c_ast.Union):
+                assert type.name in self._unions, "XXX opaque unions"
+                fields = self._unions[type.name].decls
+                fnames = [decl.name for decl in fields]
+                btypes = [self._get_btype(decl.type) for decl in fields]
+                return self._backend.get_cached_btype(
+                    'new_union_type', type.name, tuple(fnames), tuple(btypes))
         #
         raise FFIError("bad or unsupported type declaration")
 
             if name in self.ffi._structs:
                 raise FFIError("multiple declarations of struct %s" % (name,))
             self.ffi._structs[name] = node
+
+    def visit_Union(self, node):
+        if node.decls is not None:
+            name = node.name
+            if name in self.ffi._unions:
+                raise FFIError("multiple declarations of union %s" % (name,))
+            self.ffi._unions[name] = node

ffi/backend_ctypes.py

         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)]
+        union.__name__ = 'union_%s' % name
+        #
+        class CTypesUnion(CTypesData):
+            _ctype = union
+            _reftypename = 'union %s &' % name
+
+            def __init__(self, init):
+                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))
+        #
+        CTypesUnion._fix_class()
+        return CTypesUnion
+
 
 class CTypesLibrary(object):
 

testing/backend_tests.py

         py.test.raises((AttributeError, TypeError), "del s.a")
         #
         py.test.raises(ValueError, ffi.new, "struct foo", [1, 2, 3, 4])
+
+    def test_union_simple(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("union foo { int a; short b, c; };")
+        u = ffi.new("union foo")
+        assert u.a == u.b == u.c == 0
+        u.b = -23
+        assert u.b == -23
+        assert u.a != 0
+        py.test.raises(OverflowError, "u.b = 32768")
+        #
+        u = ffi.new("union foo", -2)
+        assert u.a == -2
+        py.test.raises((AttributeError, TypeError), "del u.a")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.