gpt4 book ai didi

c++ - Boost::python:对象在重写的方法中自行销毁

转载 作者:太空狗 更新时间:2023-10-29 23:06:10 26 4
gpt4 key购买 nike

在我的应用程序中嵌入 python 时,我遇到了与 python 对象生命周期相关的问题。我的应用程序将一些具有虚方法的类导出到 python,因此可以通过 python 代码对它们进行派生和扩展。应用程序正在使用 python 解释器并调用对象的虚拟方法。问题在于,当对象的引用计数器在从 C++ 代码调用的 Python 重写方法内部达到零时,解释器会立即销毁对象。因此,如果我们在对象的另一个方法中调用这样的方法,我们将得到等同于delete this 语句的行为。简单测试代码:

对象:

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

有什么好的方法可以避免这种行为吗?

最佳答案

使用 Boost.Python,这可以通过使用 boost::shared_ptr 来完成管理对象的生命周期。这通常是通过在通过 boost::python::class_ 公开 C++ 类型时指定 HeldType 来完成的。 .然而,Boost.Python 通常通过 boost::shared_ptr 提供所需的功能。在这种情况下,boost::python::wrapper类型支持转换。


这是一个完整的例子:

#include <iostream>
#include <list>
#include <string>

#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>

class Base
{
public:
virtual ~Base() { std::cout << "C++ deleted" << std::endl; }
virtual void virtFunc() {}
void rmFunc()
{
std::cout << "Precall" << std::endl;
virtFunc();
std::cout << "Postcall" << std::endl;
}
};

/// @brief Wrap Base to allow for python derived types to override virtFunc.
struct BaseWrap
: Base,
boost::python::wrapper<Base>
{
virtual ~BaseWrap() { std::cout << "Wrap deleted" << std::endl; }
void virtFunc_() { Base::virtFunc(); }
void virtFunc()
{
namespace python = boost::python;
if (python::override f = get_override("virtFunc"))
{
try { f(); }
catch (const python::error_already_set&) {}
}
}
};


std::list<boost::shared_ptr<Base> > objects;

void addObject(boost::shared_ptr<Base> o) { objects.push_back(o); }
void removeObject(boost::shared_ptr<Base> o) { objects.remove(o); }

BOOST_PYTHON_MODULE(pytest)
{
namespace python = boost::python;
python::class_<BaseWrap, boost::noncopyable >("Base", python::init<>())
.def("virtFunc", &Base::virtFunc, &BaseWrap::virtFunc_);

python::def("addObject", &addObject);
python::def("removeObject", &removeObject);
}

const char* derived_example_py =
"import pytest\n"
"\n"
"class Derived(pytest.Base):\n"
" def __init__(self, parent):\n"
" pytest.Base.__init__(self)\n"
" pytest.addObject(self)\n"
"\n"
" def __del__(self):\n"
" print(\"Python deleted\")\n"
"\n"
" def virtFunc(self):\n"
" pytest.removeObject(self)\n"
"\n"
"o1 = Derived(None)\n"
"o1 = None\n"
;

int main()
{
PyImport_AppendInittab("pytest", &initpytest);
Py_Initialize();

namespace python = boost::python;
python::object main_module = python::import("__main__");
python::object main_namespace = main_module.attr("__dict__");

try
{
exec(derived_example_py, main_namespace);
}
catch (const python::error_already_set&)
{
PyErr_Print();
}

boost::shared_ptr<Base> o(objects.front());
o->rmFunc();
std::cout << "pre reset" << std::endl;
o.reset();
std::cout << "post reset" << std::endl;
}

输出:

Precall
Postcall
pre reset
Python deleted
Wrap deleted
C++ deleted
post reset

要注意的最后一个变化是:

objects.front()->rmFunc();

被替换为:

boost::shared_ptr<Base> o(objects.front());
o->rmFunc();

这是必需的,因为 std::list::front返回对元素的引用。通过创建 shared_ptr 的拷贝,生命周期延长到 rmFunc() 调用之后。

关于c++ - Boost::python:对象在重写的方法中自行销毁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16751473/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com