在我的应用程序中嵌入 python 时,我遇到了与 python 对象生命周期相关的问题。我的应用程序将一些带有虚拟方法的类导出到 python,因此它们可以通过 python 代码进行派生和扩展。应用程序正在使用 python 解释器并调用对象的虚拟方法。问题是,当对象的引用计数器在从 c++ 代码调用的 python 覆盖方法内部达到零时,解释器会立即销毁对象。因此,如果我们在对象的另一个方法中调用这样的方法,我们将获得等同于删除该语句的行为。简单的测试代码:
目的:
class Base
{
public:
virtual ~Base()
{
std::cout << "C++ deleted" << std::endl;
std::cout.flush();
}
virtual void virtFunc()
{
}
void rmFunc()
{
std::cout << "Precall" << std::endl;
virtFunc();
std::cout << "Postcall" << std::endl;
//Segfault here, this doesn't exists.
value = 0;
}
private:
int value;
};
Boost::Python 模块库:
#include <boost/python.hpp>
#include <list>
#include "Types.h"
#include <iostream>
// Policies used for reference counting
struct add_reference_policy : boost::python::default_call_policies
{
static PyObject *postcall(PyObject *args, PyObject *result)
{
PyObject *arg = PyTuple_GET_ITEM(args, 0);
Py_INCREF(arg);
return result;
}
};
struct remove_reference_policy : boost::python::default_call_policies
{
static PyObject *postcall(PyObject *args, PyObject *result)
{
PyObject *arg = PyTuple_GET_ITEM(args, 0);
Py_DecRef(arg);
return result;
}
};
struct BaseWrap: Base, boost::python::wrapper<Base>
{
BaseWrap(): Base()
{
}
virtual ~BaseWrap()
{
std::cout << "Wrap deleted" << std::endl;
std::cout.flush();
}
void virtFunc()
{
if (boost::python::override f = get_override("virtFunc"))
{
try
{
f();
}
catch (const boost::python::error_already_set& e)
{
}
}
}
void virtFunc_()
{
Base::virtFunc();
}
};
std::list<Base*> objects;
void addObject(Base *o)
{
objects.push_back(o);
}
void removeObject(Base *o)
{
objects.remove(o);
}
BOOST_PYTHON_MODULE(pytest)
{
using namespace boost::python;
class_<BaseWrap, boost::noncopyable>("Base", init<>())
.def("virtFunc", &Base::virtFunc, &BaseWrap::virtFunc_);
def("addObject", &addObject, add_reference_policy());
def("removeObject", &removeObject, remove_reference_policy());
}
应用程序,与模块链接:
#include <boost/python.hpp>
#include <list>
#include "Types.h"
extern std::list<Base*> objects;
int main(int argc, char **argv)
{
Py_Initialize();
boost::python::object main_module = boost::python::import("__main__");
boost::python::object main_namespace = main_module.attr("__dict__");
try
{
boost::python::exec_file("fail-test.py", main_namespace);
}
catch(boost::python::error_already_set const &)
{
PyErr_Print();
}
sleep(1);
objects.front()->rmFunc();
sleep(1);
}
失败测试.py:
import pytest
class Derived(pytest.Base):
def __init__(self, parent):
pytest.Base.__init__(self)
pytest.addObject(self)
def __del__(self):
print("Python deleted")
def virtFunc(self):
pytest.removeObject(self)
o1 = Derived(None)
o1 = None
输出:
Precall
Python deleted
Wrap deleted
C++ deleted
Postcall
有什么好的方法可以避免这种行为吗?