3

我公开了一个打算在 Python 中子类化的 C++ 对象。在构造这些对象时,某些容器 C++ 端会引用这些对象。(实际上是 std::map)一个对象只能在从该容器中显式删除后才能被破坏。然而,它们由 Python 管理,因此在没有 Python 引用但仍被容器引用时被销毁。

我必须告诉 Python,当我构造对象时,我保留了对它的引用。我找不到任何简单的方法来做到这一点。我没有找到任何意味着“将对返回对象的引用计数增加一”的调用策略。我应该实施自己的呼叫政策来做到这一点吗?(我不知道如何实现调用策略)或者还有其他方法吗?

4

2 回答 2

4

我写了一个特殊的策略来增加对正在构造的对象的引用计数。

template <class Base = default_call_policies>
struct incref_return_value_policy : Base
{
    static PyObject *postcall(PyObject *args, PyObject *result)
    {
        PyObject *self = PyTuple_GET_ITEM(args, 0);
        Py_INCREF(self);
        return result;
    }
};

然后它可以用作任何其他策略:

class_<A>("A", init<>()[ incref_return_value_policy<>() ] );
于 2012-05-14T16:56:41.487 回答
1

这是一个小例子,可以满足我的要求。整个过程的中心是使用一个“holder”类,它告诉 Boost.Python 在 Python 中构造某种类型的实例时实际使用不同的类,并且它应该将 Python 对象作为第一个参数传递给自定义的任何构造函数包装类。您可以在此处找到更多信息:

http://www.boost.org/doc/libs/1_48_0/libs/python/doc/v2/class.html

(具体参见“HeldType 语义”讨论)。

#include "boost/python.hpp"

namespace bp = boost::python;

class Base {
public:
    virtual double go(int x) const = 0;
    virtual ~Base() {}
};

class PyBase : public Base {
public:
    explicit PyBase(PyObject* self) : _self(self) {
        Py_INCREF(_self); // THIS LEAKS MEMORY IF THERE'S NO DECREF!
    }
    virtual double go(int x) const {
        return bp::call_method<double>(_self, "go", x);
    }
private:
    PyObject * _self;
};

BOOST_PYTHON_MODULE(example) {
    bp::class_<Base,PyBase,boost::noncopyable>(
        "Base",
        bp::init<>() // the PyObject* arg is implicit
    ) 
        .def("go", &PyBase::go)
        ;
}

但有一些警告:

  • 如果您没有go在继承自 的 Python 中实现Base,您将收到一条关于无限递归的无用 Python 异常消息。如果有默认实现,我也不确定如何使虚函数回退到 C++(您可能可以通过查看 的代码来弄清楚bp::wrapper,它做了非常相似的事情)。

  • 如果您Base通过引用或指针从 C++ 函数返回一个对象,您将不会有一个包含PyBase对象的 Python 对象,除非您返回的实例实际上是一个 PyBase 对象(如果您考虑一下,这是有道理的)课程)。

  • 如果要按值返回,则需要添加一个以 a 作为其第一个参数的复制构造函数,然后才能从调用PyObject*中删除boost::noncopyable模板参数。bp::class_

  • 构造函数中的Py_INCREF语句告诉 Python “您的代码”正在对该对象进行额外引用。但是我不清楚你想如何添加相应的Py_DECREF: 如果你只有 a对象,那么std::map以后Base*就没有办法从中得到 aPyObject*了。

  • 解决上述问题的一种方法是拥有一个bp::handle<>or的容器bp::object,并构造其中一个 fromself放入容器中。但在这种情况下,您需要确保在程序结束之前清空这些容器,因为如果在静态析构函数时调用 Python 析构函数,您将遇到分段错误。

于 2012-05-14T02:03:32.223 回答