Skip to content
Advertisement

Calling Python function with arbitrary number (not known at compilation time) of parameters from C++

I need to make a call from C++ to a Python function of the form

def function(a, b, *c)
    print(a) # Mock example
    print(b) # Mock example
    print(c) # Mock example

Requirement: I shouldn’t modify the Python function. They are provided by users and should have that signature for consistency.

The Python.h interface provides (quoting from the link to not make you have to open another page):

  1. PyObject* PyObject_CallFunction(PyObject *callable, const char *format, ...) Return value: New reference. Call a callable Python object callable, with a variable number of C arguments. The C arguments are described using a Py_BuildValue() style format string. The format can be NULL, indicating that no arguments are provided. Return the result of the call on success, or raise an exception and return NULL on failure. This is the equivalent of the Python expression: callable(*args). Note that if you only pass PyObject * args, PyObject_CallFunctionObjArgs() is a faster alternative.

  2. PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ..., NULL) Return value: New reference. Call a callable Python object callable, with a variable number of PyObject* arguments. The arguments are provided as a variable number of parameters followed by NULL. Return the result of the call on success, or raise an exception and return NULL on failure. This is the equivalent of the Python expression: callable(arg1, arg2, …)

The number of arguments passed as part of c is not known at compilation time. At run time I have them as a PyObject * (let’s call it cc) that points to a tuple. Let’s say that the input for a I have it as a PyObject * called ca, likewise cb for b and that a PyObject * called cfunction holds the reference to function.

How can I make this call?

I have tried:

  1. PyObject_CallFunctionObjArgs(cfunction, ca, cb, cc) and similarly with the other method passing the format string. But this seems to cause what in Python would be a call of the form function(a, b, (c,)). Inside the Python code the c, instead of being a tuple of the values that I had in cc is a 1-element tuple with first entry being the tuple of elements that I had in cc.

  2. I also tried creating a new PyObject * cd, specifically a PyTupleObject containing as first two elements the content of ca and cb and the remaining elements being the elements of cc. Then I made a call like PyObject_CallFunctionObjArgs(cfunction, cd). But this time Python complains that function requires the positional argument b. I assume because the tuple in cd is being given entirely to a.

Advertisement

Answer

The following worked:

PyObject_CallFunctionObjArgs(cfunction, ca, cb, cc, NULL)

It produced the equivalent of a call in Python to

function(a,b,*c)
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement