2013-02-01 1 views
1

부스트 파이썬을 사용하여 wxWidgets의 서브 세트에 대한 바인딩을 생성하려고합니다. wxWindows의 창 개체는 자체 삭제를 처리하므로 수동으로 삭제해서는 안됩니다. 예를 들어 사용자가 닫기 버튼을 클릭하면 최상위 창을 닫으면 자동으로 삭제됩니다.부스트 파이썬이 소멸자에서 C++ 객체를 삭제하지 못하도록

이 그러나 파이썬에서 만든 창 객체에 문제가 리드 : 윈도우 인 경우 이상한 일들이 이벤트 핸들러 등으로

(http://docs.wxwidgets.org/2.8/wx_windowdeletionoverview.html 세부 사항) : 일어날 삭제 가비지 컬렉션에 C++ 객체는 항상 삭제되었습니다!

부스트 파이썬이 자신이 생성 한 C++ 객체의 소유권을 갖지 못하게 할 방법이 있습니까? 아마 생성자에 대한 호출 정책 같은 것이 있을까요?

(C++에서 삭제 된 객체를 처리하는 방법에 대해 조금 걱정이됩니다. 관련된 C++ 객체가 삭제되면 Python 객체가 어떻게됩니까? Python이 어떤 식 으로든이 사실을 알리지 않습니다.)

답변

5

이 작업은 boost::shared_ptr과 Boost.Python의 일부 설정으로 수행 할 수 있습니다.

boost::shared_ptr에는 사용자 지정 deleter를 허용하는 생성자가 있습니다. shared_ptr의 참조 횟수가 0에 도달하면 shared_ptr은 이전에 관리 된 포인터를 인수로 전달하여 고객 삭제자를 호출합니다. 이렇게하면 "no-op"또는 wxWindow::Destroy()을 호출하는 것과 같은 다른 할당 해제 전략을 사용할 수 있습니다. 파이썬 클래스를 노출하는 경우 유형은 HeldType를 통해 관리 할 수 ​​있도록

class window {}; 
void no_op(window*) {}; 
boost::shared_ptr(new window(), &no_op); 

은, Boost.Python이 있습니다. 이 경우 HeldTypeboost::shared_ptr이됩니다. 이를 통해 C++과 Python 사이에서 적절한 참조 계산이 가능하며 사용자 정의 할당 취소 전략이 허용됩니다.

  • 억제 Boost.Python 기본 초기화 (__init__를) 만들 수 :

    boost::python::class_<window, boost::shared_ptr<window>, ...>("Window", ...); 
    

    이 투명하게 함께 일을 얻기에 간계는 것입니다.

  • 사용자 정의 삭제자를 사용하여 shared_ptr을 반환하는 팩토리 함수를 호출하는 __init__ 함수를 명시 적으로 제공하십시오. 여기

destroy() 부재 기능을 파괴하고자하는 업 조롱 window 클래스이다. 참조를 생성하는 팩토리 함수 정의

class window 
{ 
... 
private: 
    ~window(); 
public: 
    void destroy(); 
}; 

커스텀 Deleter가 함께 window 객체를 세었다. 커스텀 Deleter는 참조 카운트가 0에 도달했을 때 객체에 destroy()을 호출합니다.

boost::shared_ptr<window> create_window() 
{ 
    return boost::shared_ptr<window>(
    new window(), 
    boost::mem_fn(&window::destroy)); 
} 

마지막으로 부스트를 사용하십시오.Python은 window 클래스를 노출하지만 기본 이니셜 라이저는 표시하지 않고 투명하게 create_window 팩토리 함수로 바꿉니다. 여기


boost::python::class_<window, boost::shared_ptr<window>, 
         boost::noncopyable>("Window", python::no_init) 
    .def("__init__", python::make_constructor(&create_window)); 
는 완벽한 예입니다

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

/// @brief Mockup window class. 
class window 
{ 
public: 
    window(unsigned int id) 
    : id_(id) 
    { 
    std::cout << "window::window() " << id_ << std::endl; 
    } 
    void action() { std::cout << "window::action() " << id_ << std::endl; } 
    void destroy() 
    { 
    std::cout << "window::destroy() " << id_ << std::endl; 
    delete this; 
    } 
private: 
    ~window() { std::cout << "window::~window() " << id_ << std::endl; } 
private: 
    unsigned int id_; 
}; 

/// @brief Factory function that will create reference counted window 
///  objects, that will call window::destroy() when the reference 
///  count reaches zero. 
boost::shared_ptr<window> create_window(unsigned int id) 
{ 
    return boost::shared_ptr<window>(
    new window(id), 
    boost::mem_fn(&window::destroy)); 
} 

BOOST_PYTHON_MODULE(example) { 
    namespace python = boost::python; 
    // Expose window, that will be managed by shared_ptr, and transparently 
    // constructs the window via a factory function to allow for a custom 
    // deleter. 
    python::class_<window, boost::shared_ptr<window>, 
       boost::noncopyable>("Window", python::no_init) 
    .def("__init__", python::make_constructor(&create_window)) 
    .def("action", &window::action) 
    ; 
} 

그리고 그것의 사용 :

>>> from example import Window 
>>> w1 = Window(1) 
window::window() 1 
>>> w2 = Window(2) 
window::window() 2 
>>> w3 = Window(3) 
window::window() 3 
>>> del w2 
window::destroy() 2 
window::~window() 2 
>>> w3 = None 
window::destroy() 3 
window::~window() 3 
>>> w = w1 
>>> del w1 
>>> w.action() 
window::action() 1 
>>> w = None 
window::destroy() 1 
window::~window() 1 

공지 사항 파이썬은 파이썬은 더 이상이 없습니다 일단 개체를 삭제하는 C++을 알리는 방법 인스턴스에 대한 참조 따라서이 시나리오에서 Python은 삭제 된 객체에 대해 상호 작용을 시도하지 않습니다. Object가 C++로 삭제 될 때 표현 된 걱정을 덜어주기를 바랍니다.

C++이 여전히 Python에서 활성화 된 객체를 삭제할 상황이 발생하면 opaque pointer을 사용하여 구현 클래스와 핸들 클래스를 분리하는 것이 좋습니다. 핸들 클래스는 호출을 전달하기 전에 연관된 구현 인스턴스가 삭제되었는지 검사하여 예외가 Python까지 던져 지도록합니다.

+0

와우, 덕분에 좋은 아이디어가 많이 있습니다. return_value_policy reference_existing_object와 함께 객체를 반환하는 각 클래스에 대한 정적 팩토리 메서드를 만드는 다른 솔루션을 생각해 냈습니다. 그러나 당신의 해결책은 더 좋습니다. – Jonatan

+0

나는 추상 클래스로이 접근법을 시도했지만 make_constructor가 작동하지 않는 것으로 나타났습니다. init <> 생성자를 사용하면 작동하지만, make_constructor를 사용하면 파이썬과 C++간에 앞뒤로 전달 될 때 가상 메소드의 구현이 손실됩니다. init <>로 사용자 정의 소멸자를 설정할 방법이 없기 때문에 항상 사용자 정의 소멸자가있는 shared_ptr에서 사용자 정의 shared_ptr 클래스를 파생 시켰습니다. 하지만 shared_ptr에서 파생 된 것이 확실하지는 않습니다. 어떻게 생각해? – Jonatan

+0

'shared_ptr'에서 파생되는 것을 주저합니다. 개념적으로 비 다형성 방식으로 사용될 때는 괜찮습니다. 그러나 Boost.Python은'shared_ptr'에 대해 상당한 양의 메타 프로그래밍과 처리를 수행하므로 안전하다는 것을 확실히 말할 수는 없습니다. 원래의 문제는 객체 조각의 결과처럼 들립니다. Boost.Python 솔루션은 ['bases'] (http://www.boost.org/doc/libs/1_53_0/libs/python/doc/v2/class.html#bases-spec) 및 ['wrapper'] (http://www.boost.org/doc/libs/1_53_0/libs/python/doc/v2/wrapper.html)에 나와 있습니다. –

0

실제로 auto_ptr을 사용하면 훨씬 간단한 해결책이 있으며 Boost Python FAQ에도 있습니다. Python Wiki에 대한 자세한 내용이 있습니다. 지금까지 완벽하게 작동합니다.

관련 문제