summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2016-08-22 17:40:35 +0200
committerArmin Rigo <arigo@tunes.org>2016-08-22 17:40:35 +0200
commitec18b7f424ff64bb15154bc69c7103b2b36ff072 (patch)
tree38e60aae6447568c0c8b7a0cccdb66fd8c1b2b4f
parent2401c14d0a24079b305cc3ecf36f84a1ea663d7c (diff)
parent4a14e501899044786bbe9c906f8bb3147d529c33 (diff)
downloadcffi-ec18b7f424ff64bb15154bc69c7103b2b36ff072.tar.gz
merge heads
-rw-r--r--c/_cffi_backend.c7
-rw-r--r--cffi/recompiler.py4
-rw-r--r--cffi/vengine_cpy.py2
-rw-r--r--doc/source/cdef.rst12
-rw-r--r--doc/source/ref.rst2
-rw-r--r--doc/source/using.rst51
-rw-r--r--testing/cffi1/test_recompiler.py18
7 files changed, 56 insertions, 40 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
index b358f8b..945a8dc 100644
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -3042,13 +3042,14 @@ static CDataObject *allocate_owning_object(Py_ssize_t size,
static PyObject *
convert_struct_to_owning_object(char *data, CTypeDescrObject *ct)
{
+ /* also accepts unions, for the API mode */
CDataObject *cd;
Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment);
Py_ssize_t datasize = ct->ct_size;
- if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) != CT_STRUCT) {
+ if (datasize < 0) {
PyErr_SetString(PyExc_TypeError,
- "return type is not a struct or is opaque");
+ "return type is an opaque structure or union");
return NULL;
}
cd = allocate_owning_object(dataoffset + datasize, ct);
@@ -4623,6 +4624,8 @@ static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct,
ct = ct->ct_itemdescr;
}
ffifield = fb_fill_type(fb, ct, 0);
+ if (PyErr_Occurred())
+ return NULL;
if (elements != NULL) {
for (j=0; j<flat; j++)
elements[nflat++] = ffifield;
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
index eb7c891..8e66d1c 100644
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -515,7 +515,7 @@ class Recompiler:
tovar, errcode)
return
#
- elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
+ elif isinstance(tp, model.StructOrUnionOrEnum):
# a struct (not a struct pointer) as a function argument
self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
% (tovar, self._gettypenum(tp), fromvar))
@@ -572,7 +572,7 @@ class Recompiler:
elif isinstance(tp, model.ArrayType):
return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
var, self._gettypenum(model.PointerType(tp.item)))
- elif isinstance(tp, model.StructType):
+ elif isinstance(tp, model.StructOrUnion):
if tp.fldnames is None:
raise TypeError("'%s' is used as %s, but is opaque" % (
tp._get_c_name(), context))
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
index 6bb0986..bef0f52 100644
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -308,7 +308,7 @@ class VCPythonEngine(object):
elif isinstance(tp, model.ArrayType):
return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
var, self._gettypenum(model.PointerType(tp.item)))
- elif isinstance(tp, model.StructType):
+ elif isinstance(tp, model.StructOrUnion):
if tp.fldnames is None:
raise TypeError("'%s' is used as %s, but is opaque" % (
tp._get_c_name(), context))
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
index 6f9b8a4..1e90aa8 100644
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -269,9 +269,7 @@ specialized versions, and declares the correct (unicode or not) types.
Usually, the right thing to do is to call this method with True. Be
aware (particularly on Python 2) that, afterwards, you need to pass unicode
-strings as arguments instead of byte strings. (Before cffi version 0.9,
-``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was,
-inconsistently, not defined by default.)
+strings as arguments instead of byte strings.
.. _loading-libraries:
@@ -336,7 +334,7 @@ ffibuilder.set_source(): preparing out-of-line modules
**ffibuilder.set_source(module_name, c_header_source, [\*\*keywords...])**:
prepare the ffi for producing out-of-line an external module called
-``module_name``. *New in version 1.0.*
+``module_name``.
``ffibuilder.set_source()`` by itself does not write any file, but merely
records its arguments for later. It can therefore be called before or
@@ -425,7 +423,7 @@ in the details. These places are:
declaration which doesn't use "``...``" is assumed to be exact, but this is
checked: you get an error if it is not correct.
-* *New in version 1.1:* integer types: the syntax "``typedef
+* integer types: the syntax "``typedef
int... foo_t;``" declares the type ``foo_t`` as an integer type
whose exact size and signedness is not specified. The compiler will
figure it out. (Note that this requires ``set_source()``; it does
@@ -462,8 +460,8 @@ in the details. These places are:
length is completed by the C compiler.
This is slightly different from "``int n[];``", because the latter
means that the length is not known even to the C compiler, and thus
- no attempt is made to complete it. *New in version 1.1:* support
- for multidimensional arrays: "``int n[...][...];``".
+ no attempt is made to complete it. This supports
+ multidimensional arrays: "``int n[...][...];``".
*New in version 1.2:* "``int m[][...];``", i.e. ``...`` can be used
in the innermost dimensions without being also used in the outermost
diff --git a/doc/source/ref.rst b/doc/source/ref.rst
index 25822e9..18c22f8 100644
--- a/doc/source/ref.rst
+++ b/doc/source/ref.rst
@@ -292,7 +292,7 @@ and in C, where ``&array[index]`` is just ``array + index``.
3. ``ffi.addressof(<library>, "name")`` returns the address of the
named function or global variable from the given library object.
-*New in version 1.1:* for functions, it returns a regular cdata
+For functions, it returns a regular cdata
object containing a pointer to the function.
Note that the case 1. cannot be used to take the address of a
diff --git a/doc/source/using.rst b/doc/source/using.rst
index 9d14075..b480d42 100644
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -366,8 +366,8 @@ See `Reference: conversions`__ for a similar way to pass ``struct foo_s
__ ref.html#conversions
-CFFI supports passing and returning structs to functions and callbacks.
-Example:
+CFFI supports passing and returning structs and unions to functions and
+callbacks. Example:
.. code-block:: python
@@ -377,36 +377,33 @@ Example:
myfoo = lib.function_returning_a_struct()
# `myfoo`: <cdata 'struct foo_s' owning 8 bytes>
-There are a few (obscure) limitations to the argument types and return
-type. You cannot pass directly as argument a union (but a *pointer*
-to a union is fine), nor a struct which uses bitfields (but a
-*pointer* to such a struct is fine). If you pass a struct (not a
-*pointer* to a struct), the struct type cannot have been declared with
-"``...;``" in the ``cdef()``; you need to declare it completely in
-``cdef()``. You can work around these limitations by writing a C
-function with a simpler signature in the C header code passed to
-``ffibuilder.set_source()``, and have this C function call the real one.
-
-Aside from these limitations, functions and callbacks can receive and
-return structs.
-
-For performance, API-level functions are not returned as ``<cdata>``
-objects, but as a different type (on CPython, ``<built-in
-function>``). This means you cannot e.g. pass them to some other C
+For performance, non-variadic API-level functions that you get by
+writing ``lib.some_function`` are not ``<cdata>``
+objects, but an object of a different type (on CPython, ``<built-in
+function>``). This means you cannot pass them directly to some other C
function expecting a function pointer argument. Only ``ffi.typeof()``
works on them. To get a cdata containing a regular function pointer,
-use ``ffi.addressof(lib, "name")`` (new in version 1.1).
+use ``ffi.addressof(lib, "name")``.
-Before version 1.1 (or with the deprecated ``ffi.verify()``), if you
-really need a cdata pointer to the function, use the following
-workaround:
+There are a few (obscure) limitations to the supported argument and
+return types. These limitations come from libffi and apply only to
+calling ``<cdata>`` function pointers; in other words, they don't
+apply to non-variadic ``cdef()``-declared functions if you are using
+the API mode. The limitations are that you cannot pass directly as
+argument or return type:
-.. code-block:: python
-
- ffi.cdef(""" int (*foo)(int a, int b); """)
+* a union (but a *pointer* to a union is fine);
+
+* a struct which uses bitfields (but a *pointer* to such a struct is
+ fine);
+
+* a struct that was declared with "``...``" in the ``cdef()``.
-i.e. declare them as pointer-to-function in the cdef (even if they are
-regular functions in the C code).
+In API mode, you can work around these limitations: for example, if you
+need to call such a function pointer from Python, you can instead write
+a custom C function that accepts the function pointer and the real
+arguments and that does the call from C. Then declare that custom C
+function in the ``cdef()`` and use it from Python.
Variadic function calls
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
index 59e1e3e..248c619 100644
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1962,3 +1962,21 @@ def test_function_returns_opaque():
ffi, "test_function_returns_opaque", "?")
assert str(e.value) == ("function foo: 'struct a' is used as result type,"
" but is opaque")
+
+def test_function_returns_union():
+ ffi = FFI()
+ ffi.cdef("union u1 { int a, b; }; union u1 f1(int);")
+ lib = verify(ffi, "test_function_returns_union", """
+ union u1 { int a, b; };
+ static union u1 f1(int x) { union u1 u; u.b = x; return u; }
+ """)
+ assert lib.f1(51).a == 51
+
+def test_function_returns_partial_struct():
+ ffi = FFI()
+ ffi.cdef("struct a { int a; ...; }; struct a f1(int);")
+ lib = verify(ffi, "test_function_returns_partial_struct", """
+ struct a { int b, a, c; };
+ static struct a f1(int x) { struct a s = {0}; s.a = x; return s; }
+ """)
+ assert lib.f1(52).a == 52