【发布时间】:2016-01-25 16:57:05
【问题描述】:
使用 Python/C API 在我的 C++ 代码中对 PyObject* 进行减法时遇到分段错误,我不知道为什么。我正在使用 C++ 和 Python 2.7。我正在使用新型类来实现未来的 Python 3 兼容性。
我的目标是创建一个 C++ 类 MyClass 来作为 Python 模块中定义的类的包装器。在MyClass 构造函数中,我传入Python 模块的名称,导入模块,定位类(它总是有一个预定义的名称PyClass),然后调用该类来创建它的实例。然后我将生成的PyObject* 存储在MyClass 中以备将来使用。在MyClass 析构函数中,我decref 存储了PyObject* 以避免内存泄漏。
就定位类并创建它的实例而言,我已经验证一切正常。我什至验证了我可以在其他MyClass 方法中使用存储的PyObject*,例如,访问PyClass 中的方法。但是,当析构函数执行 decref 时,会导致段错误。
这是我的代码示例。我还会在适当的时候在其他地方调用Py_Initialize() 和Py_Finalize(),为了简洁起见,我省略了一些错误检查代码:
MyPythonModule.py
class PyClass:
pass
MyClass.h
class MyClass {
public:
MyClass(const char* modulename);
~MyClass();
private:
void* _StoredPtr;
};
MyClass.cpp
#include <Python.h>
#include <iostream>
#include "MyClass.h"
MyClass::MyClass(const char* modulename) {
_StoredPtr = NULL;
PyObject *pName = NULL, *pModule = NULL, *pAttr = NULL;
// Import the Python module.
pName = PyString_FromString(modulename);
if (pName == NULL) {goto error;}
pModule = PyImport_Import(pName);
if (pModule == NULL) {goto error;}
// Create a PyClass instance and store a pointer to it.
pAttr = PyObject_GetAttrString(pModule, "PyClass");
if (pAttr == NULL) {goto error;}
_StoredPtr = (void*) PyObject_CallObject(pAttr, NULL);
Py_DECREF(pAttr);
if (_StoredPtr == NULL) {goto error;}
error:
if (PyErr_Occurred()) {PyErr_Print();}
Py_XDECREF(pName);
Py_XDECREF(pModule);
return;
}
MyClass::~MyClass() {
std::cout << "Starting destructor..." << std::endl;
Py_XDECREF((PyObject*)(_StoredPtr));
std::cout << "Destructor complete." << std::endl;
}
我知道我可以通过在析构函数中省略 Py_XDECREF() 来避免段错误,但我害怕导致内存泄漏,因为我不明白为什么会发生这种情况。我可以在其他MyClass 方法中成功使用_StoredPtr 似乎特别奇怪,但我不能对其进行decref。
我还尝试将导入模块的PyObject* 存储在MyClass 中并一直保存到_StoredPtr 被decrefed 之后,但_StoredPtr decref 仍然存在段错误。我尝试注释掉 Py_DECREF(pAttr); 行,但这没有帮助。
正如我所提到的,我可以使用_StoredPtr 检索PyClass 中的方法,并且我还尝试将这些方法存储在MyClass 中并在析构函数中取消它们。当我这样做时,我可以decref _StoredPtr,但是当我尝试decref 方法的PyObject* 时它会出现段错误。如果我使用多种方法执行此操作,则始终是导致段错误的最后一个 decref,无论我将它们按什么顺序放置。
关于这里发生了什么的任何见解?
【问题讨论】:
-
FWIW,CPython源代码使用
goto相当多...hg.python.org/cpython/file/2.7/Objects/listobject.c#l361 -
您的代码使用保留标识符,这对我来说有足够的理由不进一步查看。我也会停止在 C++ 中使用 C 风格的强制转换。也就是说,使用 Boost.Python 或类似的框架在 C++ 中编写 Python 模块,而不是解决所有再次带来的问题。
-
CPython 是 C 而不是 C++,@mgilson!在 C 中,您没有带有析构函数的对象,因此您不能使用 RAII 和类似的习惯用法。与 C++ 相比,您不能使用
goto跳过构造函数,从而限制了 C++ RAII 的适用性。我并不是说goto在 C++ 中总是不好的,使用带有自定义删除器的智能指针将是一个不错的选择。 -
@UlrichEckhardt -- 这是一个公平的观点。我没有花太多时间在 C++ 上,但我曾经使用过一个 C++ 类,这是别人为跟踪 Python 中的引用计数而编写的......
-
@erip 我正在使用 Python 文档中的示例 docs.python.org/2/c-api/intro.html#exceptions
标签: python c++ python-2.7 python-c-api