2012-05-13 2 views
2

파이썬에서 서브 클래 싱 될 C++ 객체를 노출하려고합니다. 구성된 경우 이러한 개체는 일부 컨테이너 C++ 측에서 참조합니다. (실제로 std :: map) 하나의 객체는이 컨테이너에서 명시 적으로 제거 된 후에 만 ​​소멸 될 수 있습니다. 그러나 그것들은 파이썬에 의해 관리되기 때문에 남아있는 파이썬 참조가 없을 때 파괴되지만 여전히 컨테이너에 의해 참조 된 채 남아 있습니다.boost :: python에 노출 된 생성자에 의해 생성 된 객체의 참조 카운트를 증가 시킴

필자는 객체를 만들 때 참조를 유지한다고 파이썬에게 말해야합니다. 나는 그것을 할 수있는 간단한 방법을 찾을 수 없습니다. 나는 "반환 된 객체에 대한 참조 카운트를 하나씩 증가시킨다"는 호출 정책을 찾지 못했습니다. 그렇게하기 위해 자체 호출 정책을 구현해야합니까? (나는 호출 정책을 구현하는 방법을 모릅니다) 아니면 다른 방법으로 그것을 할 수 있습니까?

+0

컨테이너에서 개체를 배치? – Dani

+0

그것을 무시하는 C++ 생성자가 파이썬에 의해 호출되었습니다. –

+0

나는 내가 말한 것이 애매하다는 것을 깨달았다. 내가 말하는 컨테이너에는 객체 자체가 아니라 참조 만 들어 있습니다. 사실 포인터의 std :: map입니다. –

답변

3

작성중인 개체에 대한 참조 횟수를 늘리는 특수 정책을 작성했습니다.

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<>() ]); 
+0

이것은 저에게 효과적입니다. 그러나 나는 메모리가 정리되는 방법에 관해서는 손해보고있다. 내 'A'클래스 소멸자가 호출되어 약간 놀랐습니다. 내가 파이썬 참조를 증가시킨 이후로 생각했지만 결코 객체를 누출시키지 않았다. 참조가 어떻게 이루어 졌는지 알아? –

+0

참조 카운트를 감소시키지 않고 Py_Finalize가 호출되기 전에 객체가 파괴되면 어딘가에 버그가있는 것 같습니다. 어쩌면 Py_Finalize 중에 개체가 파괴되었을 수도 있습니다. –

+0

Py_Finalize 이전입니다. 나는 파이썬 루프를 반복적으로 호출하여 A 객체를 생성한다. 함수의 맨 위와 A의 소멸자에서 인쇄하고 있습니다. A는 루프 반복간에 소멸됩니다. 어떤 종류의 버그가 역 참조를 일으킬 수 있습니까? –

1

다음은 내가 원하는 것으로 생각하는 작은 예제입니다. 전체 프로시 저는 Boost.Python이 파이썬에서 특정 유형의 인스턴스를 생성 할 때 실제로 다른 클래스를 사용하도록 알려주는 "holder"클래스를 사용하고 파이썬 객체를 사용자 정의의 모든 생성자에 대한 첫 번째 인수로 전달해야합니다 래퍼 클래스.

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) 
     ; 
} 

그러나 몇 가지주의 사항이있다 :

  • 당신이 Base에서 상속 파이썬에서 go를 구현하지 않는 경우, 당신은 무한 재귀에 대한 인정 파이썬 예외 메시지가 나타납니다. 또한 가상 함수를 C++로 다시 변환하는 방법을 잘 모르겠다. 기본 구현이 있다면 (아마도 비슷한 것을하는 bp::wrapper 코드를 보면 알 수있다). 당신이 참조 또는 포인터에 의한 C++ 함수에서 Base 개체를 반환하는 경우

  • , 당신은 당신이 실제로 반환로 시작하는 PyBase 객체이었다 인스턴스 않는 한 PyBase 개체를 포함하는 파이썬의 객체가되지 않습니다 (즉 의미가 있습니다 물론 그것에 대해 생각한다면). 당신이 값으로 돌아가려면

  • , 당신은 당신이 bp::class_ 통화에서 boost::noncopyable 템플릿 인수를 제거하기 전에 첫 번째 인수로 PyObject* 소요 복사 생성자를 추가해야합니다.

  • 생성자의 문은 "your code"가이 객체에 대한 추가 참조를 취한다는 것을 파이썬에게 알려줍니다. 그러나 해당하는 Py_DECREF을 추가하는 방법이 분명하지 않습니다. 가지고있는 모든 항목이 인 경우 Base* 인 경우 나중에 PyObject*을 가져올 수 없습니다.

  • 위의 한 가지 문제는 컨테이너가 bp::handle<> 또는 bp::object이고 컨테이너에 넣기 위해 self의 컨테이너 중 하나를 구성하는 것입니다. 그러나이 경우 정적 소멸자 시간에 파이썬 소멸자가 호출되면 세그먼트 화 오류가 발생하므로 프로그램이 종료되기 전에 해당 컨테이너가 비어 있는지 확인해야합니다.

+0

파이썬 파생 클래스에 대한 가상 호출을 처리하는 일반적인 방법 인 boost :: python :: wrapper를 사용할 때 솔루션을 사용하지 못했습니다. 이 방법으로 가상 메서드를 호출하는 이유는 무엇입니까? –

+0

예. boost :: python :: wrapper 메서드는 더 간단하고 자동적입니다. 그것은 구현에서 매우 유사한 접근법을 실제로 사용하지만,이 경우 (당신이 래퍼의 개인 데이터 멤버이기 때문에) 필요한 PyObject *에 대한 액세스를 제공하지 않습니다. boost :: python :: wrapper로 수행 할 수있는 모든 작업은이 방법으로도 수행 할 수 있지만 가상 함수의 C++ 기본 구현과 같은 것을 처리하기 위해 더 많은 코드를 작성해야합니다. – jbosch

+0

wrapper 클래스에서 제공하는 유틸리티를 boost :: python에서 유지하려면 정책에서 참조 카운트 처리를 사용하는 것을 선호합니다. 네 번째 포인트 인 boost :: python은 to_python 변환을 수행 할 때 PyObject *를 검색 할 수 있습니다. 그러나 그것은 심지어 필요하지 않습니다. python이 std :: map에서 객체를 제거하는 함수를 호출하면 또 다른 호출 정책은 refcounter를 감소시킵니다 : 정책은 PyObjects에 직접 액세스 할 수 있습니다. –

관련 문제