Armin Rigo avatar Armin Rigo committed 8c4195c

Two demos of how CFFI can be used to write your own C functions
using whatever API is most suitable.

Comments (0)

Files changed (2)

+import cffi
+from cffi import FFI
+
+class PythonFFI(FFI):
+
+    def __init__(self, backend=None):
+        FFI.__init__(self, backend=backend)
+        self._pyexports = {}
+
+    def pyexport(self, signature):
+        tp = self._typeof(signature, consider_function_as_funcptr=True)
+        def decorator(func):
+            name = func.__name__
+            if name in self._pyexports:
+                raise cffi.CDefError("duplicate pyexport'ed function %r"
+                                     % (name,))
+            callback_var = self.getctype(tp, name)
+            self.cdef("%s;" % callback_var)
+            self._pyexports[name] = _PyExport(tp, func)
+        return decorator
+
+    def verify(self, source='', **kwargs):
+        extras = []
+        pyexports = sorted(self._pyexports.items())
+        for name, export in pyexports:
+            callback_var = self.getctype(export.tp, name)
+            extras.append("%s;" % callback_var)
+        extras.append(source)
+        source = '\n'.join(extras)
+        lib = FFI.verify(self, source, **kwargs)
+        for name, export in pyexports:
+            cb = self.callback(export.tp, export.func)
+            export.cb = cb
+            setattr(lib, name, cb)
+        return lib
+
+
+class _PyExport(object):
+    def __init__(self, tp, func):
+        self.tp = tp
+        self.func = func
+
+
+if __name__ == '__main__':
+    ffi = PythonFFI()
+
+    @ffi.pyexport("int(int)")
+    def add1(n):
+        print n
+        return n + 1
+
+    ffi.cdef("""
+        int f(int);
+    """)
+
+    lib = ffi.verify("""
+        int f(int x) {
+            return add1(add1(x));
+        }
+    """)
+
+    assert lib.f(5) == 7
+import api
+
+ffi = api.PythonFFI()
+
+referents = []
+freelist = None
+
+def store(x):
+    global freelist
+    if freelist is None:
+        i = len(referents)
+        referents.append(x)
+    else:
+        i = freelist = referents[freelist]
+        referents[i] = x
+    return i
+
+def discard(i):
+    global freelist
+    referents[i] = freelist
+    freelist = i
+
+class Ref(object):
+    def __init__(self, x):
+        self.x = x
+    def __enter__(self):
+        self.i = i = store(self.x)
+        return i
+    def __exit__(self, *args):
+        discard(self.i)
+
+# ------------------------------------------------------------
+
+ffi.cdef("""
+    typedef int pyobj_t;
+    int sum(pyobj_t oblist, int count);
+""")
+
+@ffi.pyexport("int(pyobj_t, int)")
+def getitem(oblist, index):
+    list = referents[oblist]
+    return list[index]
+
+lib = ffi.verify("""
+    typedef int pyobj_t;
+
+    int sum(pyobj_t oblist, int count) {
+        int i, result = 0;
+        for (i=0; i<count; i++) {
+            int n = getitem(oblist, i);
+            result += n;
+        }
+        return result;
+    }
+""")
+
+with Ref([10, 20, 30, 40]) as oblist:
+    print lib.sum(oblist, 4)
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.