[pypy-commit] cffi default: Finally clean up the distinction between the C types "function" vs
arigo
noreply at buildbot.pypy.org
Tue Jun 26 18:14:51 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r530:010bdb053d10
Date: 2012-06-26 18:06 +0200
http://bitbucket.org/cffi/cffi/changeset/010bdb053d10/
Log: Finally clean up the distinction between the C types "function" vs
"pointer to function". Add a test about it to backend_tests.
diff --git a/c/_ffi_backend.c b/c/_ffi_backend.c
--- a/c/_ffi_backend.c
+++ b/c/_ffi_backend.c
@@ -2921,8 +2921,15 @@
&ellipsis))
return NULL;
- if (fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) {
- PyErr_SetString(PyExc_TypeError, "result type is of unknown size");
+ if (fresult->ct_flags & (CT_STRUCT|CT_UNION)) {
+ PyErr_SetString(PyExc_NotImplementedError,
+ "functions returning a struct or a union");
+ return NULL;
+ }
+ if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) ||
+ (fresult->ct_flags & CT_ARRAY)) {
+ PyErr_Format(PyExc_TypeError, "invalid result type: '%s'",
+ fresult->ct_name);
return NULL;
}
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1118,3 +1118,15 @@
assert list(p.a1) == ['f', 'o', 'o'] + ['\x00'] * 7
p.a1 = ['x', 'y']
assert str(p.a1) == 'xyo'
+
+def test_no_struct_return_in_func():
+ BFunc = new_function_type((), new_void_type())
+ BArray = new_array_type(new_pointer_type(BFunc), 5) # works
+ new_function_type((), BFunc) # works
+ new_function_type((), new_primitive_type("int"))
+ new_function_type((), new_pointer_type(BFunc))
+ py.test.raises(NotImplementedError, new_function_type, (),
+ new_struct_type("foo_s"))
+ py.test.raises(NotImplementedError, new_function_type, (),
+ new_union_type("foo_u"))
+ py.test.raises(TypeError, new_function_type, (), BArray)
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -85,19 +85,23 @@
assert isinstance(name, str) or name is None
return _make_ffi_library(self, name)
- def typeof(self, cdecl):
+ def typeof(self, cdecl, consider_function_as_funcptr=False):
"""Parse the C type given as a string and return the
corresponding Python type: <class 'ffi.CData<...>'>.
It can also be used on 'cdata' instance to get its C type.
"""
if isinstance(cdecl, basestring):
try:
- return self._parsed_types[cdecl]
+ btype, cfaf = self._parsed_types[cdecl]
+ if consider_function_as_funcptr and not cfaf:
+ raise KeyError
except KeyError:
- type = self._parser.parse_type(cdecl)
+ cfaf = consider_function_as_funcptr
+ type = self._parser.parse_type(cdecl,
+ consider_function_as_funcptr=cfaf)
btype = self._get_cached_btype(type)
- self._parsed_types[cdecl] = btype
- return btype
+ self._parsed_types[cdecl] = btype, cfaf
+ return btype
else:
return self._backend.typeof(cdecl)
@@ -178,7 +182,7 @@
"""
if not callable(python_callable):
raise TypeError("the 'python_callable' argument is not callable")
- BFunc = self.typeof(cdecl)
+ BFunc = self.typeof(cdecl, consider_function_as_funcptr=True)
return self._backend.callback(BFunc, python_callable, error)
def getctype(self, cdecl, replace_with=''):
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -702,7 +702,7 @@
nameargs.append('...')
nameargs = ', '.join(nameargs)
#
- class CTypesFunction(CTypesGenericPtr):
+ class CTypesFunctionPtr(CTypesGenericPtr):
__slots__ = ['_own_callback', '_name']
_ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None),
*[BArg._ctype for BArg in BArgs],
@@ -745,12 +745,18 @@
*[BArg._ctype for BArg in BArgs],
use_errno=True)
else:
- callback_ctype = CTypesFunction._ctype
+ callback_ctype = CTypesFunctionPtr._ctype
self._as_ctype_ptr = callback_ctype(callback)
self._address = ctypes.cast(self._as_ctype_ptr,
ctypes.c_void_p).value
self._own_callback = init
+ @staticmethod
+ def _initialize(ctypes_ptr, value):
+ if value:
+ raise NotImplementedError("ctypes backend: not supported: "
+ "initializers for function pointers")
+
def __repr__(self):
c_name = getattr(self, '_name', None)
if c_name:
@@ -763,7 +769,7 @@
def _get_own_repr(self):
if getattr(self, '_own_callback', None) is not None:
return 'calling %r' % (self._own_callback,)
- return super(CTypesFunction, self)._get_own_repr()
+ return super(CTypesFunctionPtr, self)._get_own_repr()
def __call__(self, *args):
if has_varargs:
@@ -789,8 +795,8 @@
result = self._as_ctype_ptr(*ctypes_args)
return BResult._from_ctypes(result)
#
- CTypesFunction._fix_class()
- return CTypesFunction
+ CTypesFunctionPtr._fix_class()
+ return CTypesFunctionPtr
def new_enum_type(self, name, enumerators, enumvalues):
mapping = dict(zip(enumerators, enumvalues))
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -98,8 +98,10 @@
def _parse_decl(self, decl):
node = decl.type
if isinstance(node, pycparser.c_ast.FuncDecl):
- self._declare('function ' + decl.name,
- self._get_type(node, name=decl.name))
+ tp = self._get_type(node, name=decl.name)
+ assert isinstance(tp, model.RawFunctionType)
+ tp = self._get_type_pointer(tp)
+ self._declare('function ' + decl.name, tp)
else:
if isinstance(node, pycparser.c_ast.Struct):
# XXX do we need self._declare in any of those?
@@ -122,11 +124,16 @@
else:
self._declare('variable ' + decl.name, tp)
- def parse_type(self, cdecl, force_pointer=False):
+ def parse_type(self, cdecl, force_pointer=False,
+ consider_function_as_funcptr=False):
ast, macros = self._parse('void __dummy(%s);' % cdecl)
assert not macros
typenode = ast.ext[-1].type.args.params[0].type
- return self._get_type(typenode, force_pointer=force_pointer)
+ type = self._get_type(typenode, force_pointer=force_pointer)
+ if consider_function_as_funcptr:
+ if isinstance(type, model.RawFunctionType):
+ type = self._get_type_pointer(type)
+ return type
def _declare(self, name, obj):
if name in self._declarations:
@@ -137,8 +144,8 @@
self._declarations[name] = obj
def _get_type_pointer(self, type, const=False):
- if isinstance(type, model.FunctionType):
- return type # "pointer-to-function" ~== "function"
+ if isinstance(type, model.RawFunctionType):
+ return model.FunctionPtrType(type.args, type.result, type.ellipsis)
if const:
return model.ConstPointerType(type)
return model.PointerType(type)
@@ -236,7 +243,7 @@
convert_array_to_pointer=True)
for argdeclnode in params]
result = self._get_type(typenode.type)
- return model.FunctionType(tuple(args), result, ellipsis)
+ return model.RawFunctionType(tuple(args), result, ellipsis)
def _is_constant_declaration(self, typenode, const=False):
if isinstance(typenode, pycparser.c_ast.ArrayDecl):
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -75,7 +75,10 @@
return ffi._backend.new_primitive_type(self.name)
-class FunctionType(BaseType):
+class RawFunctionType(BaseType):
+ # Corresponds to a C type like 'int(int)', which is the C type of
+ # a function, but not a pointer-to-function. The backend has no
+ # notion of such a type; it's used temporarily by parsing.
_attrs_ = ('args', 'result', 'ellipsis')
def __init__(self, args, result, ellipsis):
@@ -88,10 +91,21 @@
if self.ellipsis:
reprargs.append('...')
reprargs = reprargs or ['void']
- replace_with = '(*%s)(%s)' % (replace_with, ', '.join(reprargs))
+ replace_with = '(%s)(%s)' % (replace_with, ', '.join(reprargs))
return self.result._get_c_name(replace_with)
def prepare_backend_type(self, ffi):
+ from . import api
+ raise api.CDefError("cannot render the type %r: it is a function "
+ "type, not a pointer-to-function type" % (self,))
+
+
+class FunctionPtrType(RawFunctionType):
+
+ def _get_c_name(self, replace_with):
+ return RawFunctionType._get_c_name(self, '*'+replace_with)
+
+ def prepare_backend_type(self, ffi):
args = [ffi._get_cached_btype(self.result)]
for tp in self.args:
args.append(ffi._get_cached_btype(tp))
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -172,7 +172,7 @@
def convert_expr_from_c(self, tp, var):
if isinstance(tp, model.PrimitiveType):
return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
- elif isinstance(tp, (model.PointerType, model.FunctionType)):
+ elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
var, self.gettypenum(tp))
elif isinstance(tp, model.ArrayType):
@@ -193,7 +193,7 @@
# function declarations
def generate_cpy_function_decl(self, tp, name):
- assert isinstance(tp, model.FunctionType)
+ assert isinstance(tp, model.FunctionPtrType)
if tp.ellipsis:
# cannot support vararg functions better than this: check for its
# exact type (including the fixed arguments), and build it as a
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -1,6 +1,6 @@
import py
import sys, ctypes
-from cffi import FFI
+from cffi import FFI, CDefError
SIZE_OF_INT = ctypes.sizeof(ctypes.c_int)
SIZE_OF_LONG = ctypes.sizeof(ctypes.c_long)
@@ -984,3 +984,22 @@
assert ffi.getctype("int[5]", '*') == "int(*)[5]"
assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]"
assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]"
+
+ def test_array_of_func_ptr(self):
+ ffi = FFI(backend=self.Backend())
+ f = ffi.cast("int(*)(int)", 42)
+ assert f != ffi.NULL
+ py.test.raises(CDefError, ffi.cast, "int(int)", 42)
+ py.test.raises(CDefError, ffi.new, "int([5])(int)")
+ a = ffi.new("int(*[5])(int)", [f])
+ assert ffi.getctype(ffi.typeof(a)) == "int(*[5])(int)"
+ assert len(a) == 5
+ assert a[0] == f
+ assert a[1] == ffi.NULL
+ py.test.raises(TypeError, ffi.cast, "int(*)(int)[5]", 0)
+ #
+ def cb(n):
+ return n + 1
+ f = ffi.callback("int(*)(int)", cb)
+ a = ffi.new("int(*[5])(int)", [f, f])
+ assert a[1](42) == 43
diff --git a/testing/test_ctypes.py b/testing/test_ctypes.py
--- a/testing/test_ctypes.py
+++ b/testing/test_ctypes.py
@@ -1,3 +1,4 @@
+import py
from testing import backend_tests
from cffi.backend_ctypes import CTypesBackend
@@ -8,3 +9,7 @@
Backend = CTypesBackend
TypeRepr = "<class 'ffi.CData<%s>'>"
+
+ def test_array_of_func_ptr(self):
+ py.test.skip("ctypes backend: not supported: "
+ "initializers for function pointers")
More information about the pypy-commit
mailing list