Этого можно добиться с помощью boost::shared_ptr
и некоторых настройка в Boost.Python.
boost::shared_ptr
имеет конструкторы, которые принимают пользовательское средство удаления. Когда счетчик ссылок shared_ptr
достигает нуля, shared_ptr
вызывает средство удаления клиентов, передавая ранее управляемый указатель в качестве аргумента. Это позволяет использовать различные стратегии освобождения, такие как «без операции» или та, которая вызывает 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 сообщает C++ об удалении объекта только после того, как у Python больше нет ссылки на экземпляр. Таким образом, в этом сценарии Python не будет пытаться взаимодействовать с удаленным объектом. Надеюсь, это уменьшит опасения, связанные с удалением объекта в C++.
Если есть ситуации, когда C++ будет удалять объекты, которые все еще активны в Python, рассмотрите возможность использования непрозрачного указателя, чтобы разделить класс реализации и класс дескриптора. Класс дескриптора может проверить, был ли удален связанный экземпляр реализации перед переадресацией вызова, позволяя передать исключение в Python.
person
Tanner Sansbury
schedule
19.02.2013