Skip to content

Commit 19208fe

Browse files
author
Wenzel Jakob
committed
install a cleanup handler for nontrivial lambda closures
1 parent 28f98aa commit 19208fe

File tree

4 files changed

+42
-1
lines changed

4 files changed

+42
-1
lines changed

example/example5.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,29 @@ void init_ex5(py::module &m) {
7272
m.def("test_callback2", &test_callback2);
7373
m.def("test_callback3", &test_callback3);
7474
m.def("test_callback4", &test_callback4);
75+
76+
/* Test cleanup of lambda closure */
77+
78+
struct Payload {
79+
Payload() {
80+
std::cout << "Payload constructor" << std::endl;
81+
}
82+
~Payload() {
83+
std::cout << "Payload destructor" << std::endl;
84+
}
85+
Payload(const Payload &) {
86+
std::cout << "Payload copy constructor" << std::endl;
87+
}
88+
Payload(Payload &&) {
89+
std::cout << "Payload move constructor" << std::endl;
90+
}
91+
};
92+
93+
m.def("test_cleanup", []() -> std::function<void(void)> {
94+
Payload p;
95+
96+
return [p]() {
97+
/* p should be cleaned up when the returned function is garbage collected */
98+
};
99+
});
75100
}

example/example5.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from example import test_callback2
2525
from example import test_callback3
2626
from example import test_callback4
27+
from example import test_cleanup
2728

2829
def func1():
2930
print('Callback function 1 called!')
@@ -38,3 +39,5 @@ def func2(a, b, c, d):
3839
test_callback3(lambda i: i + 1)
3940
f = test_callback4()
4041
print("func(43) = %i" % f(43))
42+
43+
test_cleanup()

example/example5.ref

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,9 @@ Callback function 2 called : Hello, x, True, 5
1212
5
1313
func(43) = 44
1414
func(43) = 44
15+
Payload constructor
16+
Payload copy constructor
17+
Payload move constructor
18+
Payload destructor
19+
Payload destructor
20+
Payload destructor

include/pybind/pybind.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class cpp_function : public function {
6969
void *data = nullptr;
7070
bool is_constructor = false, is_method = false;
7171
short keywords = 0;
72+
void (*free) (void *ptr) = nullptr;
7273
return_value_policy policy = return_value_policy::automatic;
7374
std::string signature;
7475
PyObject *class_ = nullptr;
@@ -242,6 +243,9 @@ class cpp_function : public function {
242243
m_entry = new function_entry();
243244
m_entry->data = new capture { std::forward<Func>(f), std::tuple<Extra...>(std::forward<Extra>(extra)...) };
244245

246+
if (!std::is_trivially_destructible<Func>::value)
247+
m_entry->free = [](void *ptr) { delete (capture *) ptr; };
248+
245249
typedef arg_value_caster<Arg...> cast_in;
246250
typedef return_value_caster<Return> cast_out;
247251

@@ -333,7 +337,10 @@ class cpp_function : public function {
333337
static void destruct(function_entry *entry) {
334338
while (entry) {
335339
delete entry->def;
336-
operator delete(entry->data);
340+
if (entry->free)
341+
entry->free(entry->data);
342+
else
343+
operator delete(entry->data);
337344
function_entry *next = entry->next;
338345
delete entry;
339346
entry = next;

0 commit comments

Comments
 (0)