-2

I am trying to pass a 2D array from C++ to Python function but the PyObject_CallObject function is returning NULL.This happens in case of 1D array as well. It works fine when I do not pass any argument or in case the argument is just single variable and not an array. My code is as follows:

#include <stdio.h>
#include <Python.h>
#include <pyhelper.hpp>
#include <string>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h> // For capturing the output values given by detector Python inference code

using namespace std;

PyObject *PythonInitialize(string script_name, string function_name);

int main(int argc, char *argv[])
{

    if (!Py_IsInitialized())
    {
        Py_InitializeEx(0); // Initialize the Python interpreter
    }

    PyObject *PythonDetectorFunction, *PythonDetectorFunctionArguments;
    PythonDetectorFunction = PythonInitialize("test", "getsum"); //getint, getsum

    PythonDetectorFunctionArguments = PyTuple_New(1); // Reference count incremented. Need to handle the reference counting
    if (PythonDetectorFunctionArguments == NULL)
        PyErr_Print();

    if (PyArray_API == NULL)
    {
        import_array();
    }

    float data[2][4] = {{1.2, 3.4, 5.6, 7.8}, {1.2, 3.4, 5.6, 7.8}};
    npy_intp dims[2] = {2, 4};

    PythonDetectorFunctionArguments = PyArray_SimpleNewFromData(1, dims, NPY_FLOAT, data);

    PyObject *PythonDetectorFeatureMaps = PyObject_CallObject(PythonDetectorFunction, PythonDetectorFunctionArguments); // Will contain the output given by Python code

    if (PythonDetectorFeatureMaps != NULL)
        printf("PythonDetectorFeatureMaps: %p\n", PythonDetectorFeatureMaps);
    else
        printf("PythonDetectorFeatureMaps is NULL.\n");

    Py_DECREF(PythonDetectorFunction);
    Py_DECREF(PythonDetectorFunctionArguments);
    //Py_DECREF(PythonDetectorFeatureMaps);
}

PyObject *PythonInitialize(string script_name, string function_name)
{
    PyObject *pName, *pModule, *pFunc;
    string PythonCodeImportStatement = "sys.path.append(os.getcwd())";
    PyRun_SimpleString("import sys, os");
    PyRun_SimpleString(PythonCodeImportStatement.c_str()); // Relative path for the python code

    // pNmae is the name of the python script/module to be called
    pName = PyUnicode_FromString(script_name.c_str()); // Reference count incremented. Need to handle the reference counting
    if (pName == NULL)
        PyErr_Print();

    // Getting the Python module reference inside of the C code
    pModule = PyImport_Import(pName);
    if (pModule == NULL)
        PyErr_Print();

    // Python function reference
    pFunc = PyObject_GetAttrString(pModule, function_name.c_str()); // Reference count incremented. Need to handle the reference counting
    if (pFunc == NULL)
        PyErr_Print();

    // Refrence count clean-up
    Py_XDECREF(pName);
    Py_XDECREF(pModule);
    //Py_XDECREF(pFunc);
    return pFunc;
}

Python function in test.py:

def getsum(tuple1):
    print('Python function getsum() called')
    return None

Can someone point out the mistake?

surajj4837
  • 49
  • 1
  • 10

1 Answers1

-1

PyObject_CallObject takes a callable and a tuple of arguments for the callable, or NULL for no arguments. You're trying to pass it a NumPy array instead of an argument tuple.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • True, based on your suggestion I checked [this](https://cpp.hotexamples.com/examples/-/-/PyTuple_SetItem/cpp-pytuple_setitem-function-examples.html) article, it showed how to set tuples, so I updated my code accordingly. I created tuple object and assigned it size 8 instead of 1 earlier using `PyTuple_New(8)`. Assigned values like: `PyTuple_SetItem( PythonDetectorFunctionArguments, 1, PyFloat_FromDouble( data[0][1] ) );` But this also resulted in same fashion. – surajj4837 Jul 26 '21 at 11:37
  • @surajj4837: Now you're trying to pass 8 arguments to a function that only takes 1. – user2357112 Jul 26 '21 at 22:16
  • I just pasted 1 sample line of the `PyTuple_SetItem`. where the second argument is the position to fill. I m not able to paste all lines here due to word limitation. I will paste in parts. – surajj4837 Jul 27 '21 at 03:08
  • @surajj4837: That's pretty much what I thought you were doing (although I was hoping you'd use a loop). That's passing 8 arguments to a function that takes 1. – user2357112 Jul 27 '21 at 03:15
  • ```PythonDetectorFunctionArguments = PyTuple_New(8); PyTuple_SetItem( PythonDetectorFunctionArguments, 0, PyFloat_FromDouble( data[0][0] )); PyTuple_SetItem( PythonDetectorFunctionArguments, 1, PyFloat_FromDouble( data[0][1] )); PyTuple_SetItem( PythonDetectorFunctionArguments, 2, PyFloat_FromDouble( data[0][2] )); PyTuple_SetItem( PythonDetectorFunctionArguments, 3, PyFloat_FromDouble( data[0][3] )); ``` – surajj4837 Jul 27 '21 at 03:15
  • ```PyTuple_SetItem( PythonDetectorFunctionArguments, 4, PyFloat_FromDouble( data[1][0] )); PyTuple_SetItem( PythonDetectorFunctionArguments, 5, PyFloat_FromDouble( data[1][1] )); PyTuple_SetItem( PythonDetectorFunctionArguments, 6, PyFloat_FromDouble( data[1][2] )); PyTuple_SetItem( PythonDetectorFunctionArguments, 7, PyFloat_FromDouble( data[1][3] )); ``` – surajj4837 Jul 27 '21 at 03:16
  • Loop is what I will be using once it starts working properly, I have taken sample data of 8 items, actually my original data is huge. – surajj4837 Jul 27 '21 at 03:17
  • @surajj4837: Your function takes one argument. You need to build a one-element tuple whose one element is the argument you want to pass to the function. – user2357112 Jul 27 '21 at 03:17
  • Okay I thought when I assign `PythonDetectorFunctionArguments = PyTuple_New(8); ` I will be able to pass a tuple with 8 items in it. Is my understanding wrong? – surajj4837 Jul 27 '21 at 03:19
  • Each element of the tuple you pass to `PyObject_CallObject` is a separate argument to the Python callable you want to call. If you want to pass a tuple to the Python callable, you need to wrap that tuple in another tuple for `PyObject_CallObject`. – user2357112 Jul 27 '21 at 03:22
  • Thank you for your prompt replies, I implemented your latest comment and it worked. – surajj4837 Jul 27 '21 at 03:29