18

Hi I'm trying to embed python (2.7) into C++ (g++ 4.8.2) and hence call a python function from C++. This is the basic code provided in python documentation for embedding:

This is my file call_function.cpp

#include <Python.h>

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

    if (argc < 3) {
        fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
        return 1;
    }
   /* char pySearchPath[] = "/usr/include/python2.7";
    Py_SetPythonHome(pySearchPath);*/
    Py_Initialize();
    pName = PyString_FromString(argv[1]);
    /* Error checking of pName left out */

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, argv[2]);
        /* pFunc is a new reference */

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = PyTuple_New(argc - 3);
            for (i = 0; i < argc - 3; ++i) {
                pValue = PyInt_FromLong(atoi(argv[i + 3]));
                if (!pValue) {
                    Py_DECREF(pArgs);
                    Py_DECREF(pModule);
                    fprintf(stderr, "Cannot convert argument\n");
                    return 1;
                }
                /* pValue reference stolen here: */
                PyTuple_SetItem(pArgs, i, pValue);
            }
            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                printf("Result of call: %ld\n", PyInt_AsLong(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr,"Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        return 1;
    }
    Py_Finalize();
    return 0;
}

Now my python script is saved as pyfunction.py placed in the same folder as call_function.cpp.

This is pyfunction.py :

def multiply(a,b):
    print "Will compute", a, "times", b
    c = 0
    for i in range(0, a):
        c = c + b
    return c

Now using the Terminal I'm calling :

$ g++ call_function.cpp -I/usr/include/python2.7 -lpython2.7 -o call_function

(Compiles successfully without any errors) (Running the program)

$ ./call_function pyfunction multiply 2 3

(I get this ERROR):

ImportError: No module named pyfunction    
Failed to load "pyfunction"

I don't understand how this is possible. I've followed the documentation and still I'm getting the error.

How can it not find pyfunction.py when it is placed in the same directory.

user3000805
  • 287
  • 1
  • 2
  • 14

6 Answers6

22

Put the following in the C/C++ code, just after Py_Initialize();

PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append(\".\")");
Antonello
  • 6,092
  • 3
  • 31
  • 56
  • 2
    Either use `PyRun_SimpleString("import sys\n" "import os"); PyRun_SimpleString("sys.path.append( os.path.dirname(os.getcwd()) +'/project_name/')");` or put your python file in `build-...-Debug`. – Jithin Pavithran Jan 27 '17 at 20:43
  • This works properly for me, replacing the need for PYTHONPATH=. . The method in comment here using os.getcwd() is probably more cross-platform. – CourageousPotato Dec 03 '19 at 08:12
  • I think `sflee` answer is lot better as 1) is native (no interpreter running) and 2) you are protected from escaping attacks and python string injections which can lead to very serious security issues. – spinus May 11 '20 at 15:07
15

Try this one:

 $ PYTHONPATH=. ./call_function pyfunction multiply 2 3

if this won't work, try to make __init__.py file in this directory and try again.

UPDATE:

I think that PYTHONPATH is temporary solution, to test stuff. If you want to have a directory when all your embedded modules lives you have to put in your embedded interpreter something equilevant to this:

import sys
sys.path.insert(0, "./path/to/your/modules/")

You can do it probably in python in your interpreter or on C level. This will add search path in very similar manner as PYTHONPATH but it is more persistant and elegant (IMHO).

spinus
  • 5,497
  • 2
  • 20
  • 26
  • can u please explain what the above statement does? – user3000805 Jun 30 '14 at 16:03
  • Thanks! the above works. But please explain what does this? I would 'upvote' your answer but I dont even have 15 reputation lol :P – user3000805 Jun 30 '14 at 16:05
  • 1
    I think that the interactive version of Python will add the current directory to the search path for imports for convenience. This is not done automatically when embedding though, because an innocent change of directories could then lead to unintended change in the executed code. – Ulrich Eckhardt Jun 30 '14 at 17:23
  • @user3000805, Ulrich is right. Python is looking for modules in specific locations (are defined in `sys.path`). You can add more paths where python will be looking for modules. One of this ways is by environment variable called PYTHONPATH. – spinus Jun 30 '14 at 21:13
  • @spinus Can you guide me how to use PYTHONPATH=. in Eclipse? I mean I tried using __init__.py in the source folder, but no luck. I don't know where to specify the PYTHONPATH=. Moreover, this will work only on Linux right? Windows it'll fail.. – user3000805 Jul 01 '14 at 05:15
  • @user3000805, sorry I have no idea how to set this up in Eclipse. I think there is some configuration according to environment variables or to add more python search paths or library paths in pydev. Fast google searching: http://stackoverflow.com/questions/9249995/how-to-persist-pythonpath-setting-of-an-eclipse-pydev-project. It should work on windows too, you can set environment variables also on windows. – spinus Jul 01 '14 at 09:28
  • sorry, I never used Eclipse, no clue. But the second example with `sys` should work regardless of editor as it's on the app level – spinus May 11 '20 at 15:05
7

Hi to all those facing the same problem, I found the solution! setenv() is a function defined in stdlib.h which sets the environment variable. Just have to run it!

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

for more info on setenv:

$ man setenv

All the best :) Also, thanks to @spinus

user3000805
  • 287
  • 1
  • 2
  • 14
4

You can also try to include these code to your c program

Py_Initialize();
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyUnicode_FromString("."));

Learn from Here

sflee
  • 1,659
  • 5
  • 32
  • 63
  • In case of Py_LIMITED_API, PyRun_SimpleString function is not available and it becomes little bit tricky to set module path. So this is the perfect way of adding user written module path. – Tushar R. Jul 10 '20 at 18:24
1

the solution provided by spinus works if the python file does not import any additional python-library.

However, if a python file imports an additional library, lets say numpy, the above code crashes as follows:

:~/programs/python$ ./a.out myModule multiply 4 3
Traceback (most recent call last):
  File "/home/a/programs/python/myModule.py", line 1, in <module>
    import numpy
ImportError: No module named 'numpy'
Failed to load "myModule"

As a remark, the import of the python-library from C does not work:

PyObject *pNumpy = PyUnicode_FromString("numpy");
PyObject *pModuleA = PyImport_Import(pNumpy); 

Does someone know how to call from C python-functions, which depend of some other python-libraries?

Humberto
  • 65
  • 1
  • 8
  • Problem solved. The C-compilation command was referring to the wrong python version. Therefore, the Py_Initialize() was calculating the module search default path wrongly. However, this could be tweaked by editing the environment variable PYTHONHOME. – Humberto May 13 '15 at 07:26
  • 1
    what do I have to change PYTHONHOME to make it work? – pd176 Nov 27 '15 at 10:35
  • @Humberto, what is the env variable set to in your case? In my case I have: `/Users/my.name/.pyenv/shims/python` and include file at: `/Users/my.name/.pyenv/versions/2.7.13/envs/ipython2` – sAguinaga May 17 '18 at 18:10
0

For anyone else having this problem:

Are you sure that your .py file lies in the same directory where C++ executable is?

I was programming in CLion and forgot that executable lies in cmake-build-debug. So I added .py file in project directory and no no surprise I was getting the same error ImportError. I placed .py file to cmake-build-debug (executable file by default lies there), used answers from this question and everything worked!

bolt
  • 387
  • 2
  • 6
  • 16