2

I am working on a Python code embedding in c++ helloworld program, necessary additional include/library directories are properly set up.

When I use Local Windows Debugger, it shows "Hello World" correctly. But if I double click project.exe, it says project.exe has stopped working.

Does anyone know what kind of configurations or steps to make so that project.exe shows "Hello World" when double clicked??

Code goes like the following:

main.cpp

#include <iostream>
#include <Python.h> 
#include <string.h>
#include <stdlib.h>
using namespace std;
int main() 
{ 
    Py_Initialize(); 
    PyRun_SimpleString("import sys");   
    PyRun_SimpleString("sys.path.append('./')");
    PyObject *pModule = PyImport_ImportModule("helloworld");
    PyObject *pFunc = PyObject_GetAttrString(pModule, "printHello");
    PyEval_CallObject(pFunc, NULL);
    Py_Finalize();
    return 0;
}

helloworld.py

def printHello():
   print("Hello World!")
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Tiger Hu
  • 47
  • 7

1 Answers1

1

Shot in the dark:

  • You're not checking the return values for your calls, specially the ones returning pointers
  • running with the debugger isn't using the same directory as the "exe-clicking" method

you should check the return value of pModule. My guess is that since you're in a different directory, the import fails, so the PyImport_ImportModule function returns NULL (python raises exceptions, but not in this context as this is a C API with its limitations)

This is fragile (and possibly useless):

sys.path.append('./')

You don't know what the current directory is. It would be better to make it relative to the current executable, or configurable with an argument or an environment variable. You could make it relative to the current executable, see Finding current executable's path without /proc/self/exe or Get path of executable

Now, when you try to use this null pointer, the program crashes. Start with this:

PyObject *pModule = PyImport_ImportModule("helloworld");
if (pModule == NULL)
{
   std::cout << "could not import module\n";
   exit(1);
}

(same goes for the attribute fetch: always protect your calls, or better: wrap them in a C++ method that throws exceptions).

#include <string>
#include <stdexcept>

PyObject *safe_PyImport_ImportModule(const std::string &module_name)
{
   PyObject *pModule = PyImport_ImportModule(module_name.c_str());
   if (pModule == NULL) // c++11 purists would put "nullptr"
   {
       std::cout << "cannot import " << module_name << '\n';
       throw std::runtime_error("Import error: "+module_name);
   }
   return pModule;
}
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • I added throws exceptions and changed system path to absolute path sys.path.append('C:/project/'), and exe-clicking method worked!! Now absolute path is kind of bad, I hope to replace it by relative path ( directory of "project.sln" ), but don't know how. Could you give me some help? – Tiger Hu Mar 04 '19 at 03:39
  • @TigerHu See https://stackoverflow.com/questions/1528298/get-path-of-executable. It's not pretty, unfortunately. – Maxpm Mar 04 '19 at 03:43
  • maybe trying to check `sys.executable` would work and return the executable name in a portable way. – Jean-François Fabre Mar 04 '19 at 04:15