[pypy-commit] cffi default: Document how to indirectly define callbacks using unsupported

arigo noreply at buildbot.pypy.org
Fri Dec 14 21:50:08 CET 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1098:448276ce8477
Date: 2012-12-14 21:49 +0100
http://bitbucket.org/cffi/cffi/changeset/448276ce8477/

Log:	Document how to indirectly define callbacks using unsupported
	features

diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -963,11 +963,36 @@
 the callback to remain valid forever, store the object in a fresh global
 variable somewhere.)
 
-Note that callbacks of a variadic function type are not supported.
+Note that callbacks of a variadic function type are not supported.  A
+workaround is to add custom C code.  In the following example, a
+callback gets a first argument that counts how many extra ``int``
+arguments are passed::
+
+    ffi.cdef("""
+        int (*python_callback)(int how_many, int *values);
+        void *const c_callback;   /* pass this ptr to C routines */
+    """)
+    lib = ffi.verify("""
+        #include <stdarg.h>
+        #include <alloca.h>
+        static int (*python_callback)(int how_many, int *values);
+        static int c_callback(int how_many, ...) {
+            va_list ap;
+            /* collect the "..." arguments into the values[] array */
+            int i, *values = alloca(how_many * sizeof(int));
+            va_start(ap, how_many);
+            for (i=0; i<how_many; i++)
+                values[i] = va_arg(ap, int);
+            va_end(ap);
+            return python_callback(how_many, values);
+        }
+    """)
+    lib.python_callback = python_callback
 
 Windows: you can't yet specify the calling convention of callbacks.
 (For regular calls, the correct calling convention should be
-automatically inferred by the C backend.)
+automatically inferred by the C backend.)  Use an indirection, like
+in the example just above.
 
 Be careful when writing the Python callback function: if it returns an
 object of the wrong type, or more generally raises an exception, then
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -1437,3 +1437,39 @@
     assert res == ord(b"g")
     res = lib.myfunc(ffi.cast("int *", p))
     assert res == ord(b"g")
+
+def test_callback_indirection():
+    ffi = FFI()
+    ffi.cdef("""
+        int (*python_callback)(int how_many, int *values);
+        void *const c_callback;   /* pass this ptr to C routines */
+        int some_c_function(void *cb);
+    """)
+    lib = ffi.verify("""
+        #include <stdarg.h>
+        #include <alloca.h>
+        static int (*python_callback)(int how_many, int *values);
+        static int c_callback(int how_many, ...) {
+            va_list ap;
+            /* collect the "..." arguments into the values[] array */
+            int i, *values = alloca(how_many * sizeof(int));
+            va_start(ap, how_many);
+            for (i=0; i<how_many; i++)
+                values[i] = va_arg(ap, int);
+            va_end(ap);
+            return python_callback(how_many, values);
+        }
+        int some_c_function(int(*cb)(int,...)) {
+            return cb(2, 10, 20) + cb(3, 30, 40, 50);
+        }
+    """)
+    seen = []
+    @ffi.callback("int(int, int*)")
+    def python_callback(how_many, values):
+        seen.append([values[i] for i in range(how_many)])
+        return 42
+    lib.python_callback = python_callback
+
+    res = lib.some_c_function(lib.c_callback)
+    assert res == 84
+    assert seen == [[10, 20], [30, 40, 50]]


More information about the pypy-commit mailing list