Commits

Armin Rigo committed 448276c

Document how to indirectly define callbacks using unsupported
features

Comments (0)

Files changed (2)

doc/source/index.rst

 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

testing/test_verify.py

     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]]
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.