install a cleanup handler for nontrivial lambda closures

This commit is contained in:
Wenzel Jakob 2015-10-13 17:37:25 +02:00
parent 28f98aa298
commit 19208fe9a4
4 changed files with 42 additions and 1 deletions

View File

@ -72,4 +72,29 @@ void init_ex5(py::module &m) {
m.def("test_callback2", &test_callback2); m.def("test_callback2", &test_callback2);
m.def("test_callback3", &test_callback3); m.def("test_callback3", &test_callback3);
m.def("test_callback4", &test_callback4); m.def("test_callback4", &test_callback4);
/* Test cleanup of lambda closure */
struct Payload {
Payload() {
std::cout << "Payload constructor" << std::endl;
}
~Payload() {
std::cout << "Payload destructor" << std::endl;
}
Payload(const Payload &) {
std::cout << "Payload copy constructor" << std::endl;
}
Payload(Payload &&) {
std::cout << "Payload move constructor" << std::endl;
}
};
m.def("test_cleanup", []() -> std::function<void(void)> {
Payload p;
return [p]() {
/* p should be cleaned up when the returned function is garbage collected */
};
});
} }

View File

@ -24,6 +24,7 @@ from example import test_callback1
from example import test_callback2 from example import test_callback2
from example import test_callback3 from example import test_callback3
from example import test_callback4 from example import test_callback4
from example import test_cleanup
def func1(): def func1():
print('Callback function 1 called!') print('Callback function 1 called!')
@ -38,3 +39,5 @@ print(test_callback2(func2))
test_callback3(lambda i: i + 1) test_callback3(lambda i: i + 1)
f = test_callback4() f = test_callback4()
print("func(43) = %i" % f(43)) print("func(43) = %i" % f(43))
test_cleanup()

View File

@ -12,3 +12,9 @@ Callback function 2 called : Hello, x, True, 5
5 5
func(43) = 44 func(43) = 44
func(43) = 44 func(43) = 44
Payload constructor
Payload copy constructor
Payload move constructor
Payload destructor
Payload destructor
Payload destructor

View File

@ -69,6 +69,7 @@ private:
void *data = nullptr; void *data = nullptr;
bool is_constructor = false, is_method = false; bool is_constructor = false, is_method = false;
short keywords = 0; short keywords = 0;
void (*free) (void *ptr) = nullptr;
return_value_policy policy = return_value_policy::automatic; return_value_policy policy = return_value_policy::automatic;
std::string signature; std::string signature;
PyObject *class_ = nullptr; PyObject *class_ = nullptr;
@ -242,6 +243,9 @@ private:
m_entry = new function_entry(); m_entry = new function_entry();
m_entry->data = new capture { std::forward<Func>(f), std::tuple<Extra...>(std::forward<Extra>(extra)...) }; m_entry->data = new capture { std::forward<Func>(f), std::tuple<Extra...>(std::forward<Extra>(extra)...) };
if (!std::is_trivially_destructible<Func>::value)
m_entry->free = [](void *ptr) { delete (capture *) ptr; };
typedef arg_value_caster<Arg...> cast_in; typedef arg_value_caster<Arg...> cast_in;
typedef return_value_caster<Return> cast_out; typedef return_value_caster<Return> cast_out;
@ -333,7 +337,10 @@ private:
static void destruct(function_entry *entry) { static void destruct(function_entry *entry) {
while (entry) { while (entry) {
delete entry->def; delete entry->def;
operator delete(entry->data); if (entry->free)
entry->free(entry->data);
else
operator delete(entry->data);
function_entry *next = entry->next; function_entry *next = entry->next;
delete entry; delete entry;
entry = next; entry = next;