Commits

Armin Rigo committed 05e4ecd

Test and fix for issue #51: unlike what is documented, we can't pass a
list argument to a function expecting a "foo *" argument. It doesn't
work on CPython (but works with vengine_gen or on PyPy).

Comments (0)

Files changed (3)

        or N > 0 if conversion would require N bytes of storage.
     */
     Py_ssize_t length, datasize;
-    CTypeDescrObject *ctitem = ctptr->ct_itemdescr;
-
+    CTypeDescrObject *ctitem;
+
+    if (CData_Check(init))
+        goto convert_default;
+
+    ctitem = ctptr->ct_itemdescr;
     /* XXX some code duplication, how to avoid it? */
     if (PyBytes_Check(init)) {
         /* from a string: just returning the string here is fine.
         else
             argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i);
 
-        if ((argtype->ct_flags & CT_POINTER) && !CData_Check(obj)) {
+        if (argtype->ct_flags & CT_POINTER) {
             char *tmpbuf;
             Py_ssize_t datasize = _prepare_pointer_call_argument(
                                             argtype, obj, (char **)data);
 /************************************************************/
 /* Functions used by '_cffi_N.so', the generated modules    */
 
-static char *_cffi_to_c_char_p(PyObject *obj)
-{
-    if (PyBytes_Check(obj)) {
-        return PyBytes_AS_STRING(obj);
-    }
-    if (CData_Check(obj)) {
-        return ((CDataObject *)obj)->c_data;
-    }
-    _convert_error(obj, "char * or void *", "compatible pointer");
-    return NULL;
-}
-
 #define _cffi_to_c_SIGNED_FN(RETURNTYPE, SIZE)                          \
 static RETURNTYPE _cffi_to_c_i##SIZE(PyObject *obj) {                   \
     PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj);                      \
 #endif
 
 static void *cffi_exports[] = {
-    _cffi_to_c_char_p,
+    0,
     _cffi_to_c_i8,
     _cffi_to_c_u8,
     _cffi_to_c_i16,
 #endif
     _cffi_to_c_long_double,
     _cffi_to_c__Bool,
+    _prepare_pointer_call_argument,
+    convert_array_from_object,
 };
 
 /************************************************************/
             errvalue = '-1'
         #
         elif isinstance(tp, model.PointerType):
-            if ((isinstance(tp.totype, model.PrimitiveType) and
-                    tp.totype.name == 'char') or
-                 isinstance(tp.totype, model.VoidType)):
-                converter = '_cffi_to_c_char_p'
-            else:
-                converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
-                extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
-            errvalue = 'NULL'
+            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
+                                                    tovar, errcode)
+            return
         #
         elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
             # a struct (not a struct pointer) as a function argument
             tovar, tp.get_c_name(''), errvalue))
         self._prnt('    %s;' % errcode)
 
+    def _extra_local_variables(self, tp, localvars):
+        if isinstance(tp, model.PointerType):
+            localvars.add('Py_ssize_t datasize')
+
+    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
+        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
+        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
+            self._gettypenum(tp), fromvar, tovar))
+        self._prnt('  if (datasize != 0) {')
+        self._prnt('    if (datasize < 0)')
+        self._prnt('      %s;' % errcode)
+        self._prnt('    %s = alloca(datasize);' % (tovar,))
+        self._prnt('    memset((void *)%s, 0, datasize);' % (tovar,))
+        self._prnt('    if (_cffi_convert_array_from_object('
+                   '(char *)%s, _cffi_type(%d), %s) < 0)' % (
+            tovar, self._gettypenum(tp), fromvar))
+        self._prnt('      %s;' % errcode)
+        self._prnt('  }')
+
     def _convert_expr_from_c(self, tp, var, context):
         if isinstance(tp, model.PrimitiveType):
             if tp.is_integer_type():
         context = 'argument of %s' % name
         for i, type in enumerate(tp.args):
             prnt('  %s;' % type.get_c_name(' x%d' % i, context))
+        #
+        localvars = set()
+        for type in tp.args:
+            self._extra_local_variables(type, localvars)
+        for decl in localvars:
+            prnt('  %s;' % (decl,))
+        #
         if not isinstance(tp.result, model.VoidType):
             result_code = 'result = '
             context = 'result of %s' % name
      sizeof(type) == 8 ? _cffi_to_c_u64(o) :                             \
      (Py_FatalError("unsupported size for type " #type), 0))
 
-#define _cffi_to_c_char_p                                                \
-                 ((char *(*)(PyObject *))_cffi_exports[0])
 #define _cffi_to_c_i8                                                    \
                  ((int(*)(PyObject *))_cffi_exports[1])
 #define _cffi_to_c_u8                                                    \
     ((long double(*)(PyObject *))_cffi_exports[21])
 #define _cffi_to_c__Bool                                                 \
     ((_Bool(*)(PyObject *))_cffi_exports[22])
-#define _CFFI_NUM_EXPORTS 23
+#define _cffi_prepare_pointer_call_argument                              \
+    ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23])
+#define _cffi_convert_array_from_object                                  \
+    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24])
+#define _CFFI_NUM_EXPORTS 25
 
 typedef struct _ctypedescr CTypeDescrObject;
 

testing/test_verify.py

     res = lib.some_c_function(lib.c_callback)
     assert res == 84
     assert seen == [[10, 20], [30, 40, 50]]
+
+def test_floatstar_argument():
+    ffi = FFI()
+    ffi.cdef("float sum3floats(float *);")
+    lib = ffi.verify("""
+        float sum3floats(float *f) {
+            return f[0] + f[1] + f[2];
+        }
+    """)
+    assert lib.sum3floats((1.5, 2.5, 3.5)) == 7.5
+
+def test_charstar_argument():
+    ffi = FFI()
+    ffi.cdef("char sum3chars(char *);")
+    lib = ffi.verify("""
+        char sum3chars(char *f) {
+            return f[0] + f[1] + f[2];
+        }
+    """)
+    assert lib.sum3chars(('\x10', '\x20', '\x30')) == '\x60'