在Qt项目中调用Python脚本时如何避免分段错误

问题描述 投票:0回答:3

我试图在C ++下调用Python中的函数。

这是我的尝试:

void myFuncion()
{
    PyObject* fExportar = nullptr;
    PyObject* modulo = nullptr;
    PyObject* pName = nullptr;
    const char *scriptDirectoryName = "path/of/my/pyfile";
    Py_Initialize();
    PyObject *sysPath = PySys_GetObject("path");
    PyObject *path = PyUnicode_FromString(scriptDirectoryName);
    int result = PyList_Insert(sysPath, 0, path);
    if (result == 0 )//0 if ok, -1 if error
    {
        pName = PyUnicode_FromString("exportarXLS");//exportarXLS.py
        modulo = PyImport_Import(pName);
        Py_DECREF(path);
        if (modulo)
        {           
                fExportar = PyObject_GetAttrString(modulo, "exportar");//it crahs here
                Py_DECREF(modulo);                
                if (fExportar)
                {
                     //call the function
                }
            }        
    }
    else
    {
        PyErr_Print();
    }
    Py_Finalize();
}
}

问题是如果python脚本有任何错误的import,我的C ++程序崩溃。在这种情况下,我怀疑我试图使用无效版本的PyQt4。

这是模块:(exportarXLS.py)

#!/usr/bin/python3

from PyQt4 import QtCore, QtGui, QtSql

def exportar():
    print ("hello, I am probing")

现在我的问题:

现在我开始探索开发加载python插件函数的步骤,我想知道如果有人想要添加错误的import脚本,我将如何避免崩溃

我也尝试将问题线包含在try/catch块中,但它不起作用。

编辑:

我忘了说它只发生在我的Qt项目中如果我试图运行我的功能,我可以加载模块的错误,但它不会崩溃。我编辑了这篇文章

python c++ pyqt
3个回答
1
投票

鸭胶带解决方案:

void signal_handler(int signal)
{
  std::cout << "Usefull information" std::endl; 
  exit(1);
}
...
std::signal(SIGSEGV, signal_handler);

Doc:https://en.cppreference.com/w/cpp/utility/program/signal

我认为这种解决方案只能在调试中使用


1
投票

最好通过找出SEGV的根本原因来处理问题,因为应用程序的状态可能会因触发而严重损坏。

如果你想尝试以半结构化的方式捕获SEGV,那么你可以使用类似示例代码的东西,它使用sigsetjmpsiglongjmp

#include <python3.7m/Python.h> // That's my python
#include <setjmp.h>
#include <signal.h>

static sigjmp_buf env;

static void
catch_segv(int func)
{
    siglongjmp(env, 1);
}

int myFunction()
{
    PyObject* fExportar = nullptr;
    PyObject* modulo = nullptr;
    PyObject* pName = nullptr;
    const char *scriptDirectoryName = "."; // NOTE: I changed the path for me
    Py_InitializeEx(1); // NOTE: skip signal handlers being registered - for embedding
    PyObject *sysPath = PySys_GetObject("path");
    PyObject *path = PyUnicode_FromString(scriptDirectoryName);
    int result = PyList_Insert(sysPath, 0, path);
    if (result == 0 )//0 if ok, -1 if error
    {
        pName = PyUnicode_FromString("beep");//exportarXLS.py
        modulo = PyImport_Import(pName);
        Py_DECREF(path);
        if (modulo)
        {
            // redirect segv handler here:
            sig_t old = signal(SIGSEGV, catch_segv);
            // record an environment to return to with siglongjmp
            if (sigsetjmp(env, 1)) { // returns 0 on setting up, 1 when called with siglongjmp(env, 1)
                // handler called
                Py_Finalize();
                signal(SIGSEGV, old); // restore old handler
                return 1; // return to caller
            } else {
                // this triggers a segv (for the test)
                (reinterpret_cast<sig_t>(0))(1);
                fExportar = PyObject_GetAttrString(modulo, "beep");//it crahs here
                Py_DECREF(modulo);
                if (fExportar)
                {
                     //call the function
                }
            }
            signal(SIGSEGV, old); // restore old handler
        }
    }
    else
    {
        PyErr_Print();
    }
    Py_Finalize();
    return 0; // return success.
}

int main(int argc, char **argv)
{
    return myFunction();
}

1
投票

这个问题听起来很熟悉,我甚至可以用你的代码重现它:

如果不止一次调用myFunction(),会发生多次导入模块的情况。根据文件,这可能会导致问题:

“如果多次调用初始化例程,有些扩展可能无法正常工作;如果应用程序多次调用Py_Initialize()和Py_Finalize(),就会发生这种情况。” https://docs.python.org/2/c-api/init.html

因此,如果您的应用中出现这种情况,则解决方法是仅初始化Python解释器一次:

#include <iostream>
#include <Python/Python.h>

void myFuncion()
{
    PyObject* fExportar = nullptr;
    PyObject* modulo = nullptr;
    PyObject* pName = nullptr;
    const char *scriptDirectoryName = "path/of/my/pyfile";

    PyObject *sysPath = PySys_GetObject("path");
    PyObject *path = PyUnicode_FromString(scriptDirectoryName);
    int result = PyList_Insert(sysPath, 0, path);
    if (result == 0 )//0 if ok, -1 if error
    {
        pName = PyUnicode_FromString("exportarXLS");//exportarXLS.py
        modulo = PyImport_Import(pName);
        Py_DECREF(path);
        if (modulo)
        {           
                fExportar = PyObject_GetAttrString(modulo, "exportar");//it crahs here
                Py_DECREF(modulo);                
                if (fExportar)
                {
                     //call the function
                }
            }        
    }
    else
    {
        PyErr_Print();
    }

}

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

    Py_Initialize();

    myFuncion();
    myFuncion();

    // what ever

    Py_Finalize();

    return 0;
}

编辑:“我甚至可以重现它”,这意味着我可以通过导入numpy在另一条线上让它崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.