[pypy-commit] cffi default: Partly untested: support for callbacks with different calling
arigo
noreply at buildbot.pypy.org
Tue Jul 17 00:27:23 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r661:cf812c61a579
Date: 2012-07-16 15:14 +0200
http://bitbucket.org/cffi/cffi/changeset/cf812c61a579/
Log: Partly untested: support for callbacks with different calling
convensions on Windows.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -3321,6 +3321,40 @@
return NULL;
}
+static PyObject *b_get_function_type_args(PyObject *self, PyObject *arg)
+{
+ CTypeDescrObject *fct = (CTypeDescrObject *)arg;
+ PyObject *x, *args, *res, *ellipsis, *conv;
+ Py_ssize_t end;
+
+ if (!CTypeDescr_Check(arg) || !(fct->ct_flags & CT_FUNCTIONPTR)) {
+ PyErr_SetString(PyExc_TypeError, "expected a 'ctype' funcptr object");
+ return NULL;
+ }
+
+ args = NULL;
+ ellipsis = NULL;
+ conv = NULL;
+ x = NULL;
+
+ end = PyTuple_GET_SIZE(fct->ct_stuff);
+ args = PyTuple_GetSlice(fct->ct_stuff, 2, end);
+ if (args == NULL)
+ goto error;
+ res = PyTuple_GET_ITEM(fct->ct_stuff, 1);
+ ellipsis = PyInt_FromLong(fct->ct_extra == NULL);
+ if (ellipsis == NULL)
+ goto error;
+ conv = PyTuple_GET_ITEM(fct->ct_stuff, 0);
+ x = PyTuple_Pack(4, args, res, ellipsis, conv);
+ /* fall-through */
+ error:
+ Py_XDECREF(args);
+ Py_XDECREF(ellipsis);
+ Py_XDECREF(conv);
+ return x;
+}
+
static void invoke_callback(ffi_cif *cif, void *result, void **args,
void *userdata)
{
@@ -3392,9 +3426,10 @@
cif_description_t *cif_descr;
ffi_closure *closure;
Py_ssize_t size;
-
- if (!PyArg_ParseTuple(args, "O!O|O:callback", &CTypeDescr_Type, &ct, &ob,
- &error_ob))
+ long fabi = FFI_DEFAULT_ABI;
+
+ if (!PyArg_ParseTuple(args, "O!O|Ol:callback", &CTypeDescr_Type, &ct, &ob,
+ &error_ob, &fabi))
return NULL;
if (!(ct->ct_flags & CT_FUNCTIONPTR)) {
@@ -3864,6 +3899,7 @@
{"new_union_type", b_new_union_type, METH_VARARGS},
{"complete_struct_or_union", b_complete_struct_or_union, METH_VARARGS},
{"new_function_type", b_new_function_type, METH_VARARGS},
+ {"get_function_type_args", b_get_function_type_args, METH_O},
{"new_enum_type", b_new_enum_type, METH_VARARGS},
{"_getfields", b__getfields, METH_O},
{"newp", b_newp, METH_VARARGS},
@@ -4061,6 +4097,9 @@
v = PyInt_FromLong(FFI_DEFAULT_ABI);
if (v == NULL || PyModule_AddObject(m, "FFI_DEFAULT_ABI", v) < 0)
return;
+ Py_INCREF(v);
+ if (PyModule_AddObject(m, "FFI_CDECL", v) < 0) /*win32 name*/
+ return;
init_errno();
}
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -681,6 +681,19 @@
BFunc = new_function_type((BStruct,), BShort, False)
assert repr(BFunc) == "<ctype 'short(*)(struct foo)'>"
+def test_get_function_type_args():
+ BChar = new_primitive_type("char")
+ BShort = new_primitive_type("short")
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [('a1', BChar, -1),
+ ('a2', BShort, -1)])
+ BFunc = new_function_type((BStruct,), BShort, False)
+ a, b, c, d = get_function_type_args(BFunc)
+ assert a == (BStruct,)
+ assert b == BShort
+ assert c == False
+ assert d == FFI_DEFAULT_ABI
+
def test_function_void_result():
BVoid = new_void_type()
BInt = new_primitive_type("int")
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -182,7 +182,7 @@
"""
return self._backend.buffer(cdata, size)
- def callback(self, cdecl, python_callable, error=None):
+ def callback(self, cdecl, python_callable, error=None, conv=None):
"""Return a callback object. 'cdecl' must name a C function pointer
type. The callback invokes the specified 'python_callable'.
Important: the callback object must be manually kept alive for as
@@ -191,8 +191,32 @@
if not callable(python_callable):
raise TypeError("the 'python_callable' argument is not callable")
BFunc = self.typeof(cdecl, consider_function_as_funcptr=True)
+ if conv is not None:
+ BFunc = self._functype_with_conv(BFunc, conv)
return self._backend.callback(BFunc, python_callable, error)
+ def _functype_with_conv(self, BFunc, conv):
+ abiname = '%s' % (conv.upper(),)
+ try:
+ abi = getattr(self._backend, 'FFI_' + abiname)
+ except AttributeError:
+ raise ValueError("the calling convention %r is unknown to "
+ "the backend" % (abiname,))
+ if abi == self._backend.FFI_DEFAULT_ABI:
+ return BFunc
+ # xxx only for _cffi_backend.c so far
+ try:
+ bfunc_abi_cache = self._bfunc_abi_cache
+ return bfunc_abi_cache[BFunc, abi]
+ except AttributeError:
+ bfunc_abi_cache = self._bfunc_abi_cache = {}
+ except KeyError:
+ pass
+ args, res, ellipsis, _ = self._backend.get_function_type_args(BFunc)
+ result = self._backend.new_function_type(args, res, ellipsis, abi)
+ bfunc_abi_cache[BFunc, abi] = result
+ return result
+
def getctype(self, cdecl, replace_with=''):
"""Return a string giving the C type 'cdecl', which may be itself
a string or a <ctype> object. If 'replace_with' is given, it gives
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -714,9 +714,14 @@
Note that callbacks of a variadic function type are not supported.
-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.)
+Windows: for regular calls, the correct calling convention should be
+automatically inferred by the C backend, but that doesn't work for
+callbacks. The default calling convention is "cdecl", like in C;
+if needed, you must force the calling convention with the keyword
+argument ``conv``::
+
+ ffi.callback("int(*)(int, int)", myfunc, conv="stdcall")
+ ffi.callback("int(*)(int, int)", myfunc, conv="cdecl") # default
Be careful when writing the Python callback function: if it returns an
object of the wrong type, or more generally raises an exception, then
@@ -737,7 +742,9 @@
``ffi.errno``: the value of ``errno`` received from the most recent C call
in this thread, and passed to the following C call, is available via
-reads and writes of the property ``ffi.errno``.
+reads and writes of the property ``ffi.errno``. On Windows we also save
+and restore the ``GetLastError()`` value, but to access it you need to
+declare and call the ``GetLastError()`` function as usual.
``ffi.buffer(pointer, [size])``: return a read-write buffer object that
references the raw C data pointed to by the given 'cdata', of 'size'
More information about the pypy-commit
mailing list