【问题标题】:How to create and use a python object stored as a c++ object?如何创建和使用存储为 c++ 对象的 python 对象?
【发布时间】:2020-05-11 23:59:22
【问题描述】:

所以正如标题所暗示的那样,我正在开发一个 c++ 项目,我需要调用一个 python 模块,将它保存为一个对象,并多次调用它的一个方法。您可以在下面找到包含 python 对象的类的代码。目前,它是在 for 循环中实现的,该循环多次调用该方法。实例化类工作正常,以及对类的第一次调用。但是,在 for 循环的第一个循环完成后,程序会崩溃,并出现“free(): invalid size”或有时“double free or corruption”的错误。我尝试使用 valgrind 来尝试追踪内存泄漏,但是我得到了很多我不太了解的 pythonCApi 调用的痕迹。

#include <python2.7/Python.h>
#include <iostream>
#include <algorithm>
#include "predictor.hpp"

using namespace std;


predictor::predictor()
{
  Py_Initialize();

  pName = PyString_FromString("predictor");

  pModule = PyImport_Import(pName);

  Py_XDECREF(pName);

  if (pModule == nullptr) {
    PyErr_Print();
    std::cerr << "Fails to import the module predictor, check installation.\n";
  }

  // dict is a borrowed reference.
  dict = PyModule_GetDict(pModule);
  if (dict == nullptr) {
    PyErr_Print();
    std::cerr << "Fails to get the dictionary, check predictor installation.\n";
    Py_XDECREF(pModule);
  }
  Py_XDECREF(pModule);

  // Builds the name of a callable class
  python_class = PyDict_GetItemString(dict, "Predictor");
  if (python_class == nullptr || python_class == NULL) {
    PyErr_Print();
    std::cerr << "Fails to get the Python class, check predictor installation.\n";
    Py_XDECREF(dict);
  }
  Py_XDECREF(dict);

  // Creates an instance of the class
  if (PyCallable_Check(python_class)) {
    object = PyObject_CallObject(python_class, nullptr);
    if (object == NULL)
    {
        cerr << "Fails to create object.";
        Py_XDECREF(python_class);
    }
    Py_XDECREF(python_class);
} else {
    PyErr_Print();
    std::cout << "Cannot instantiate the Python class" << std::endl;
    Py_XDECREF(python_class);
}

pMethod = PyString_FromString("predict_all");

}


predictor::~predictor()
{

  Py_XDECREF(pMethod);

  Py_XDECREF(object);
  Py_Finalize();

}


long predictor::predict(string rule)
{

  PyObject *pRule = PyString_FromString(rule.c_str());
  PyObject *value = PyObject_CallMethodObjArgs(object, pMethod, pRule, NULL);

  long endValue = PyInt_AsLong(value);

  if (endValue == -1)
  {
    if(!PyErr_Occurred())
    {

        PyErr_Print();
        cerr << "";

        Py_XDECREF(value);
        Py_XDECREF(pRule); 

        return NULL;
    }

    //PyErr_Print();

  }

  Py_XDECREF(value);
  Py_XDECREF(pRule); 

  return endValue;}

【问题讨论】:

    标签: python c++ python-embedding


    【解决方案1】:

    编写 Python C/C++ 代码最关键的部分是正确地进行引用计数。 Python 区分不同类型的引用,即newstolenborrowed 引用。

    对于您调用的每个 API 函数,您必须查看文档以查看它返回的引用类型(如果有)。

    新的引用属于调用者,因此使用Py_XDECREF 通过减少引用计数来释放对象是正确的。确保不要多次调用Py_XDECREF,除非您在两者之间增加了引用计数。在您的错误处理中,Py_XDECREF(pModule) 发生了两次,例如,因为您在错误情况下没有返回,您只需继续。

    借用的引用归其他人所有,并且引用计数并未为您增加。因此,调用Py_XDECREF 仅在您自己增加引用计数之前才有效。

    PyModule_GetDict(pModule) 返回一个借用的引用。您不增加引用计数,但稍后使用Py_XDECREF(dict) 减少它。 PyDict_GetItemString(dict, "predictor") 也是如此,它返回一个借用的引用,但你用 Py_XDECREF(python_class) 递减它。

    我的假设是,在这两种情况下(dictpython_class),借用的引用都归您使用PyImport_Import(pName) 导入的模块pModule 所有。因此,只要您使用由pModule 拥有的借用引用,您就很有可能不能减少pModule 引用计数。一旦你不再使用那些借来的引用,就释放pModulePy_XDECREF。或者,您可以增加借用引用的引用计数,但只要您保留pModule,就没有必要这样做。

    【讨论】:

    • 好的,所以您的建议解决了第一个内存错误(通过删除所有借用代码的 DECREFS,并删除重复的 decrefs)。但是,在那之后我收到一个错误,说 free() 大小无效。是和我这里的代码有关,还是别的什么。
    • 我重新创建了您的示例。当我为dictpython_class 删除不正确的PyXDECREFs 时,我无法再重现内存问题。 valgrind 确实报告了内存错误。以前,确实如此。在最后一次使用逻辑上归它所有的借用引用之后,我还移动了pModulePyXDECREF - 所以在实例创建块之后。在那种情况下没有可识别的错误,但根据所有权,这是正确的做法。许多示例似乎在取消初始化之前保留了 pModule 引用,但就引用而言,我认为没有必要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-14
    相关资源
    最近更新 更多