Commits

Armin Rigo committed e8f9466

vararg function calls.

Comments (0)

Files changed (3)

         if key in self._ffi._declarations:
             node = self._ffi._declarations[key]
             name = node.type.declname
+            params = list(node.args.params)
+            ellipsis = (len(params) > 0 and
+                        isinstance(params[-1], pycparser.c_ast.EllipsisParam))
+            if ellipsis:
+                params.pop()
             args = [self._ffi._get_btype(argdeclnode.type)
-                    for argdeclnode in node.args.params]
+                    for argdeclnode in params]
             result = self._ffi._get_btype(node.type)
-            value = self._backendlib.load_function(name, args, result)
+            value = self._backendlib.load_function(name, args, result,
+                                                   varargs=ellipsis)
             setattr(self, name, value)
             return value
         raise AttributeError(name)

ffi/backend_ctypes.py

     def _to_ctypes(value):
         raise TypeError
 
+    @classmethod
+    def _arg_to_ctypes(cls, value):
+        res = cls._to_ctypes(value)
+        if not isinstance(res, cls._ctype):
+            res = cls._ctype(res)
+        return res
+
     @staticmethod
     def _from_ctypes(ctypes_value):
         raise TypeError
             _reftypename = 'void &'
             def __init__(self, value=None):
                 raise TypeError("%s cannot be instantiated" % (CTypesVoid,))
+            @staticmethod
+            def _from_ctypes(novalue):
+                return None
         CTypesVoid._fix_class()
         return CTypesVoid
 
                     address = value._convert_to_address_of(BItem)
                 return ctypes.cast(address, CTypesPtr._ctype)
 
+            if kind == 'constcharp':
+                @classmethod
+                def _arg_to_ctypes(cls, value):
+                    if isinstance(value, str):
+                        return ctypes.c_char_p(value)
+                    else:
+                        return super(CTypesPtr, cls)._arg_to_ctypes(value)
+
             @staticmethod
             def _from_ctypes(ctypes_ptr):
                 if not ctypes_ptr:
                 self._address = ctypes.addressof(ctypes_ptr.contents)
                 self._as_ctype_ptr = ctypes_ptr
                 return self
+
+            def _convert_to_address_of(self, BClass):
+                if BItem is BClass or BClass is CTypesVoid:
+                    return self._address
+                else:
+                    return CTypesData._convert_to_address_of(self, BClass)
         #
+        CTypesVoid = self.get_cached_btype('new_void_type')
         CTypesPtr._fix_class()
         return CTypesPtr
 
         self.cdll = cdll
         self.void_type = self.backend.get_cached_btype('new_void_type')
 
-    def load_function(self, name, bargs, bresult):
-        func = getattr(self.cdll, name)
-        func.argtypes = [barg._ctype for barg in bargs]
-        if bresult is self.void_type:
-            func.restype = None
+    def load_function(self, name, BArgs, BResult, varargs=False):
+        c_func = getattr(self.cdll, name)
+        if BResult is self.void_type:
+            c_func.restype = None
         else:
-            func.restype = bresult._ctype
-        return func
+            c_func.restype = BResult._ctype
+        #
+        def call(*args):
+            if varargs:
+                assert len(args) >= len(BArgs)
+                extraargs = args[len(BArgs):]
+                args = args[:len(BArgs)]
+            else:
+                assert len(args) == len(BArgs)
+            ctypes_args = []
+            for arg, BArg in zip(args, BArgs):
+                ctypes_args.append(BArg._arg_to_ctypes(arg))
+            if varargs:
+                for i, arg in enumerate(extraargs):
+                    if not isinstance(arg, CTypesData):
+                        raise TypeError("argument %d needs to be a cdata" %
+                                        (1 + len(BArgs) + i,))
+                    ctypes_args.append(arg._arg_to_ctypes(arg))
+            result = c_func(*ctypes_args)
+            return BResult._from_ctypes(result)
+        #
+        call.func_name = name
+        return call
 
 
 def _identity(x):

testing/test_math.py

         ffi.C.fflush(None)
     res = fd.getvalue()
     assert res == 'hello\n  world\n'
+
+def test_vararg():
+    ffi = FFI()
+    ffi.cdef("""
+       int printf(const char *format, ...);
+       int fflush(void *);
+    """)
+    with FdWriteCapture() as fd:
+        ffi.C.printf("hello\n")
+        ffi.C.printf("hello, %s!\n", ffi.new("const char *", "world"))
+        ffi.C.printf("hello int %d long %ld long long %lld\n",
+                     ffi.new("int", 42),
+                     ffi.new("long", 84),
+                     ffi.new("long long", 168))
+        ffi.C.fflush(None)
+    res = fd.getvalue()
+    assert res == ("hello\n"
+                   "hello, world!\n"
+                   "hello int 42 long 84 long long 168\n")
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.