Variadic function calls

16 views
Skip to first unread message

Paul Nader

unread,
Jun 27, 2023, 3:31:40 AM6/27/23
to python-cffi
Hi,

I have a c variadic function as follows:

char * foo(char* name, char* bar, ...);

and I wrap it in a python function as follows:

def call_foo(name, bar, **kwargs):
    return lib.foo(name, bar, kwargs)

When I call it as follows:

call_foo(name='name', bar='bar', 'x'=x, 'y'=y, 'z'=z)

I get TypeError: argument 3 passed in the variadic part needs to be a cdata object (got dict)

What is the correct way of passing kwargs?



Armin Rigo

unread,
Jun 27, 2023, 3:45:03 AM6/27/23
to pytho...@googlegroups.com
Hi,
You can't pass a dictionary to a C function. What would it mean?
From the C point of view, a variadic function takes an unknown number
of extra arguments of unknown types. The C code is written to expect
specific types based on some other information, like the `%`
specifiers in the format string for `printf()`, for example.

You need to make sure you know the correct expected types, and cast
them to the right type and pass them as extra arguments to
`lib.foo()`. For things like integers, you cannot pass directly a
Python integer; you must pass `ffi.cast("int", 42)` for example, or
`ffi.cast("long", 42)`, as appropriate.

https://cffi.readthedocs.io/en/latest/using.html#variadic-function-calls


A bientôt,

Armin Rigo
Message has been deleted

Paul Nader

unread,
Jun 27, 2023, 6:46:16 AM6/27/23
to python-cffi
Hi,

Yes, i was aware of the ffi.cast requirement and was doing it but passing a dict as you pointed out. 
I rewrote it as follows:

def call_foo(name, bar, **kwargs):
    params = ''
    args = [kwarg for kwarg in kwargs.values()]
    for i, _ in enumerate(args):
       params += f', args[{i}]'   
    invoke = f'lib.foo(name, bar{params})'
    return eval(invoke)

Thanks,
Paul

Armin Rigo

unread,
Jun 27, 2023, 7:40:36 AM6/27/23
to pytho...@googlegroups.com, paul....@gmail.com
Hi,

On Tue, 27 Jun 2023 at 12:46, Paul Nader <paul....@gmail.com> wrote:
> Yes, i was aware of the ffi.cast requirement and was doing it but passing a dict as you pointed out.
> I rewrote it as follows:
>
> def call_foo(name, bar, **kwargs):
> params = ''
> args = [kwarg for kwarg in kwargs.values()]
> for i, _ in enumerate(args):
> params += f', args[{i}]'
> invoke = f'lib.foo(name, bar{params})'
> return eval(invoke)

Equivalent code: in Python you can call a function with a "*":

def call_foo(name, bar, **kwargs):
return lib.foo(name, bar, *kwargs.values())



Armin Rigo

Armin Rigo

unread,
Jun 27, 2023, 7:46:42 AM6/27/23
to pytho...@googlegroups.com, paul....@gmail.com
Hi again,

On Tue, 27 Jun 2023 at 13:39, Armin Rigo <armin...@gmail.com> wrote:
> Equivalent code: in Python you can call a function with a "*":

Ah, sorry, you probably know that but were confused because calling
with "**" didn't work. Calling with "*" works, because it expands to
positional arguments. But calling with "**" or passing explicit
keyword arguments doesn't work for CFFI-exposed C functions (for both
fixed and variadic parameters).

Armin Rigo
Reply all
Reply to author
Forward
0 new messages