これは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 に例外をスローできるようにすることができます。