-2

I'm embedding a Python module in my C++ code and I am using Python/C API. I need to call a Python module function and get the results. The function gets an unsigned integer and a double as input arguments and outputs a list. I use the following to call the function:

unsigned int num_units = 10;
double max_time = 15.12;
PyObject *output_list = PyObject_CallMethod(sample_object, "get_list", 
                                            "(I)", num_units, "(d)", max_time);

What I notice from Python side when adding print statements inside get_list function is that the arguments are not being passed to the function. I think I'm not getting the syntax right maybe?

Update: I tried @Ruzihm's suggestion with the following syntax initially without success. It turns out that there were other syntactical issues in the python code which prevented proper execution of the code and none of my error checks were catching it. After fixing the issues, the code ran flawlessly.

PyObject *output_list = PyObject_CallMethod(sample_object, "get_list",
                                            "Id",num_units, max_time);

Also I tried using CallMethodObjArgs with the following code which worked as well:

PyObject *func_obj, *num_units_obj, *max_time_obj;

num_units_obj = PyLong_FromUnsignedLong(num_units);
max_time_obj = PyFloat_FromDouble(max_time);
func_obj = PyUnicode_FromString("get_list");

PyObject *output_list = PyObject_CallMethodObjArgs(sample_object, 
                                                   func_obj, num_units_obj,
                                                   max_time_obj, NULL);

Py_DECREF(func_obj);
Py_DECREF(num_units_obj);
Py_DECREF(max_time_obj);

Also the Python function definition looks like this:

def get_list(self, num_units, max_time):

Any help is very much appreciated.

4
  • "the arguments are not being passed to the function" - Well, what is passed then? This really needs an minimal reproducible example - we the emphasis on "reproducible" rather than drip feeding little snippets Commented Feb 25, 2021 at 19:57
  • Did you find a solution to this problem? Commented Feb 26, 2021 at 16:44
  • 1
    @Ruzihm your answer works for me too (and PyObject_CallMethodObjArgs also works for me in exactly the form in the question). The problem is clearly in the code that OP hasn't shown. Commented Feb 27, 2021 at 16:30
  • @Ruzihm, as you guys mentioned the problem was somewhere else and none of the checks that I had in my code were catching the issue. Both your method and the other method (using CallMethodObjArgs) I suggested works fine now. If you can please add the other method to your answer, I will accept yours. Commented Mar 1, 2021 at 23:56

1 Answer 1

2

There should be exactly one format string (which may be NULL) followed by any/all inputs. Parenthesis are for specifying tuples of size zero or one, which you don't mention needing. So, just do this:

PyObject *output_list = PyObject_CallMethod(sample_object, 
        "get_list", "Id", num_units, max_time);

Full example:

foobar.py

class foobar():
     def get_list(self, num_units, max_time):
         print(num_units)
         print(max_time)

foobar.cpp

#define PY_SSIZE_T_CLEAN
#include "python3.6m/Python.h"
#include <iostream>

int main() {
    PyObject *module, *dict, *python_class, *sample_object; 

    setenv("PYTHONPATH", ".", 1);
    Py_Initialize();  

    module = PyImport_ImportModule("foobar");
    if (module == nullptr)
    {
        std::cout << "Failed to import module.";
        return 1;
    }
 
    dict = PyModule_GetDict(module);
    if (dict == nullptr)
    {
        std::cout << "Failed to get module dict.";
        return 1;
    }
    Py_DECREF(module);
 
    python_class = PyDict_GetItemString(dict, "foobar");
    if (python_class == nullptr)
    {
        std::cout << "Failed to get class.";
        return 1;
    }
    Py_DECREF(dict);
 
    sample_object = PyObject_CallObject(python_class, nullptr);
    if (sample_object == nullptr)
    {
        std::cout << "Failed to instantiate object.";
        return 1;
    }
    Py_DECREF(python_class);
 

    unsigned int num_units = 10;
    double max_time = 15.12;
 
    PyObject *output_list = PyObject_CallMethod(sample_object, "get_list",
                                             "Id", num_units, max_time);

    /* Alternatively, use PyObject_CallMethodObjArgs
    PyObject *func_obj, *num_units_obj, *max_time_obj;

    num_units_obj = PyLong_FromUnsignedLong(num_units);
    max_time_obj = PyFloat_FromDouble(max_time);
    func_obj = PyUnicode_FromString("get_list");

    PyObject *output_list = PyObject_CallMethodObjArgs(sample_object, 
                                                   func_obj, num_units_obj,
                                                   max_time_obj, NULL);

    Py_DECREF(func_obj);
    Py_DECREF(num_units_obj);
    Py_DECREF(max_time_obj);
    */
}

(Based on this answer's code)

Output

$ g++ foobar.cpp -lpython3.6m;./a.out
10
15.12
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a lot. Tried your suggestion and yet didn't work. I see the method get_list is called (have a print to file statement there). However none of arguments gets a value that is passed to it.
BTW, here is get_list definition line: def get_list(self, num_units, maxTime):
@Amir works for me
Your suggestion does work. For the sake of completeness, please update your answer with CallMethodObjArgs and I'll accept your answer. Thanks for your help again.
@Amir Glad I could help. I included your code from the answer

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.