2

BoostPythonを使用してwxWidgetsのサブセットのバインディングを作成しています。wxWidgetsのウィンドウオブジェクトは、独自の削除を処理するため、手動で削除しないでください。たとえば、ユーザーが閉じるボタンをクリックしてトップレベルのウィンドウを閉じると、自動的に削除されます。ウィンドウ削除されると、イベントハンドラーなどで奇妙なことが起こります。

(詳細:http ://docs.wxwidgets.org/2.8/wx_windowdeletionoverview.html )

ただし、これにより、Pythonで作成されたウィンドウオブジェクトに問題が発生します。ガベージコレクションでは、C++オブジェクトは常に削除されます。

BoostPythonに作成したC++オブジェクトの所有権を取得しないように指示する方法はありますか?おそらくコンストラクターの呼び出しポリシーのようなものですか?

(また、C++から削除されたオブジェクトの処理方法について少し心配しています。関連付けられたC++オブジェクトが削除された場合、Pythonオブジェクトはどうなりますか?Pythonはこれについて通知されません。)

4

2 に答える 2

7

これはboost::shared_ptr、Boost.Python でいくつかの設定を行うことで実現できます。

boost::shared_ptrカスタムの削除を受け入れるコンストラクターがあります。のshared_ptr参照カウントがゼロにshared_ptrなると、 は以前に管理されていたポインタを引数として渡して、顧客のデリータを呼び出します。これにより、「no-op」や を呼び出すものなど、さまざまな割り当て解除戦略を使用できますwxWindow::Destroy()

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

クラスを Python に公開する場合、Boost.Python では、HeldType. この場合、 はHeldTypeになりますboost::shared_ptr。これにより、C++ と Python の間で適切な参照カウントが発生し、カスタムの割り当て解除戦略が可能になります。

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

これらを透過的に連携させる秘訣は次のとおりです。

  • Boost.Python がデフォルトの初期化子を作成しないようにします ( __init__)。
  • カスタムのデリータで を__init__返すファクトリ関数を呼び出す関数を明示的に提供します。shared_ptr

これは、メンバー関数windowを介してのみ破棄されることを目的としたモックアップされたクラスです。destroy()

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

windowカスタム デリータを使用して参照カウント オブジェクトを作成するファクトリ関数が定義されています。destroy()参照カウントがゼロに なると、カスタムのデリータがオブジェクトで呼び出されます。

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

最後に、Boost.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

Python がインスタンスへの参照を持たなくなった場合にのみ、オブジェクトを削除するように Python が C++ に通知する方法に注意してください。したがって、このシナリオでは、Python は削除されたオブジェクトを操作しようとしません。これにより、C++ でオブジェクトが削除されたときに発生する懸念が軽減されることを願っています。

Python でまだアクティブなオブジェクトを C++ が削除する状況がある場合は、不透明なポインターを使用して実装クラスとハンドル クラスを分離することを検討してください。ハンドル クラスは、呼び出しを転送する前に、関連付けられた実装インスタンスが削除されているかどうかを確認し、Python に例外をスローできるようにすることができます。

于 2013-02-19T21:09:55.743 に答える