mehdi_bm
mehdi_bm

Reputation: 409

integrate python with C++

I want to integrate python code with C++, in which c++ source code call a function/class in python. I specifically need to pass array from c++ code to python code and get another array from python in c++ source code. To illustarte, the main.cc

// call_function.c - A sample of calling 
// python functions from C code
// 

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <Python.h>
#include <iostream>
#include "numpy/arrayobject.h"


int main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pRet;

    const int SIZE{ 10 };
    npy_intp dims = SIZE;
    const int ND{ 1 };

    double* a = new double[10]();
    double* b = new double[10]();

    if (a == NULL)
    {
      exit(1);
    }
    std::cout << a[1] << std::endl;

    // Initialize the Python Interpreter
    Py_Initialize();
    import_array();
    PyRun_SimpleString("import sys\n");
    PyRun_SimpleString("sys.path.append(\"/home/mehdi\")");

    // Build the name object
    pName = PyUnicode_FromString("pytst");
    if (pName == NULL) exit(1);

    // Load the module object
    pModule = PyImport_Import(pName);
    if (pModule == NULL) exit(1);

    Py_DECREF(pName);
    if (!pModule){
        std::cout << "mymodule can not be imported" << std::endl;
        return 1;
    }

    // pDict is a borrowed reference 
    pDict = PyModule_GetDict(pModule);
    if (pDict == NULL) exit(1);

    PyObject *pArraya = PyArray_SimpleNewFromData(ND, &dims, NPY_DOUBLE, 
                                                  reinterpret_cast<void*>(a));

    import_array();

    PyArrayObject *np_arra = reinterpret_cast<PyArrayObject*>(pArraya);
    PyObject *pArrayb = PyArray_SimpleNewFromData(
        ND, &dims, NPY_LONGDOUBLE, reinterpret_cast<void*>(b));

    PyArrayObject *np_arrb = reinterpret_cast<PyArrayObject*>(pArrayb);
    if (!pArraya || !pArrayb)
        std::cout << "Error" << std::endl;


    // pFunc is also a borrowed reference 
    pFunc = PyDict_GetItemString(pDict, "multiply");

    if (PyCallable_Check(pFunc)) 
    {
        pRet = PyObject_CallObject(pFunc, pArraya);
    } else 
    {
        PyErr_Print();
    }

    if (!pRet)
        std::cout << "error" << std::endl;
    if (!PyArray_Check(pRet)) {
        std::cerr <<  " did not return an array." << std::endl;
    }


    if (pRet == NULL) exit(1);

    std::cout << 9 << std::endl;
    PyArrayObject *np_ret = reinterpret_cast<PyArrayObject*>(pRet);

    double* c_out;
    //int len = PyArray_SHAPE(np_ret)[0];
    std::cout << 10 << std::endl;

    c_out = reinterpret_cast<double*>(PyArray_DATA(np_ret));
    // Clean up
    Py_DECREF(pModule);
    Py_DECREF(pName);

    // Finish the Python Interpreter
    Py_Finalize();

    return 0;
}
}

pytst.py

import numpy as np
def multiply(a, b):
    c = np.array([])
    for i in range(10):
      c = np.append(c, a[i] + b[i])
    return c

and to compile it I use

g++ -Wall main.cc -o main -I/usr/include/python3.8 -I /usr/local/lib/python3.5/dist-packages/numpy/core/include.

I converted double* to PyObject* to pass from python to c++. but I get NULL for pRet (return array from python code to c++). I'd really appreciate if anyone let me know how this communication between c++ and python code take place.

Upvotes: 2

Views: 509

Answers (3)

mehdi_bm
mehdi_bm

Reputation: 409

I decided to use pybind11 to integrate python and c++. A simple and good reference for learning working with the library, you can use this tutorial

Upvotes: 1

Den-Jason
Den-Jason

Reputation: 2573

Python does everything using variant types based on PyObject; you will need to create PyObject instances representing lists containing the doubles, e.g.

PyObject* alist = PyList_New(10);
for (i= 0;  i < 10;  i++)
{
    PyList_SetItem(alist, 0, PyFloat_FromDouble(a[i]));
}
...etc...

[edit] when you call a Python method you need to pass the parameters as a tuple, e.g.:

PyObject* parameters = PyTuple_New(2); 
PyTuple_SetItem(parameters, 0, pArraya);
PyTuple_SetItem(parameters, 1, pArrayb);
pRet = PyObject_CallObject(pFunc, parameters);
Py_DECREF(parameters);

Also look at some online examples, e.g. https://gist.github.com/nad2000/9f69c5096e10c34acddb

I recommend using Cython, it's much much nicer to use than CPython and there are plenty of tutorials online.

Upvotes: 1

vivekkushwaha39
vivekkushwaha39

Reputation: 31

Looking at the prototype of function PyObject_CallObject 2nd parameter should be of type PyObject* so i dont think double can be directly passed to PyObject_CallObject.

Call a callable Python object callable, with arguments given by the tuple args. If no arguments are needed, then args can be NULL.

See full doc at https://docs.python.org/3/c-api/object.html#c.PyObject_CallObject

Upvotes: 0

Related Questions