Skip to content

Commit 5b00921

Browse files
committed
Ensure the custom deleter is copied back to the unique pointer.
Feels like there's still a gap around the raw pointer flavour, but this at least makes the unit test of the previous commit succeed.
1 parent 7cc1e1f commit 5b00921

File tree

2 files changed

+63
-10
lines changed

2 files changed

+63
-10
lines changed

include/pybind11/detail/smart_holder_type_casters.h

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,29 @@ struct shared_ptr_trampoline_self_life_support {
464464
}
465465
};
466466

467+
template <typename D, bool>
468+
struct delete_assigner {
469+
static void assign(D &, bool, std::function<void(void *)> &, void (*)(void *) &) {
470+
// Situation where the deleter cannot be assigned from either del_fun or del_ptr.
471+
// This covers the default deleters and the like.
472+
}
473+
};
474+
475+
template <typename D>
476+
struct delete_assigner<D, true> {
477+
static void assign(D &deleter,
478+
bool use_del_fun,
479+
std::function<void(void *)> &del_fun,
480+
void (*del_ptr)(void *) &) {
481+
// Situation where D is assignable from del_fun.
482+
if (use_del_fun) {
483+
deleter = std::move(del_fun);
484+
} else {
485+
deleter = del_ptr;
486+
}
487+
}
488+
};
489+
467490
template <typename T>
468491
struct smart_holder_type_caster_load {
469492
using holder_type = pybindit::memory::smart_holder;
@@ -616,13 +639,46 @@ struct smart_holder_type_caster_load {
616639
"instance cannot safely be transferred to C++.");
617640
}
618641

642+
// Need to extract the deleter from the holder such that it can be passed back to the
643+
// unique pointer.
644+
645+
// Temporary variable to store the extracted deleter in.
646+
D extracted_deleter;
647+
648+
// In smart_holder_poc, the deleter is always stored in a guarded delete.
649+
// The guarded delete's std::function<void(void*)> actually points at the custom_deleter
650+
// type, so we can verify it is of the custom deleter type and finally extract its deleter.
651+
auto *gd = std::get_deleter<pybindit::memory::guarded_delete>(holder().vptr);
652+
if (gd) {
653+
if (gd->use_del_fun) {
654+
using custom_deleter_D = pybindit::memory::custom_deleter<T, D>;
655+
const auto &custom_deleter_ptr = gd->del_fun.template target<custom_deleter_D>();
656+
if (!custom_deleter_ptr) {
657+
throw cast_error("Deletion function is not stored in custom deleter, cannot \
658+
extract the deleter correctly.");
659+
}
660+
// Now that we have confirmed the type of the deleter matches the desired return
661+
// value we can extract the function.
662+
extracted_deleter = std::move(custom_deleter_ptr->deleter);
663+
} else {
664+
// The del_ptr attribute of the guarded deleter does not provide any type
665+
// information that can be used to confirm it is convertible to D. SFINEA here is
666+
// necessary to ensure that if D is not constructible from a void(void*) pointer,
667+
// it does not cause compilation failures. If this hits an non-convertible type at
668+
// compile time it throws.
669+
constexpr bool assignable = std::is_constructible<D, decltype(gd->del_fun)>::value;
670+
delete_assigner<D, assignable>::assign(
671+
extracted_deleter, gd->use_del_fun, gd->del_fun, gd->del_ptr);
672+
}
673+
}
674+
619675
// Critical transfer-of-ownership section. This must stay together.
620676
if (self_life_support != nullptr) {
621677
holder().disown();
622678
} else {
623679
holder().release_ownership();
624680
}
625-
auto result = std::unique_ptr<T, D>(raw_type_ptr);
681+
auto result = std::unique_ptr<T, D>(raw_type_ptr, std::move(extracted_deleter));
626682
if (self_life_support != nullptr) {
627683
self_life_support->activate_life_support(load_impl.loaded_v_h);
628684
} else {

tests/test_class_sh_basic.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,16 @@ std::string pass_udcp(std::unique_ptr<atyp const, sddc> obj) { return "pass_udcp
6767

6868
struct custom_deleter
6969
{
70-
std::string delete_txt;
71-
void operator()(atyp* p) const
72-
{
73-
std::default_delete<atyp>()(p);
74-
}
70+
std::string delete_txt;
71+
void operator()(atyp* p) const
72+
{
73+
std::default_delete<atyp>()(p);
74+
}
7575
};
7676

7777
std::unique_ptr<atyp, custom_deleter> rtrn_udmp_del() { return std::unique_ptr<atyp, custom_deleter>(new atyp{"rtrn_udmp_del"}, custom_deleter{"udmp_deleter"}); }
7878

79-
std::string pass_udmp_del(std::unique_ptr<atyp, custom_deleter> obj) {
80-
const auto d = obj.get_deleter();
81-
return "pass_udmp_del:" + obj->mtxt + "," + d.delete_txt;
82-
}
79+
std::string pass_udmp_del(std::unique_ptr<atyp, custom_deleter> obj) { return "pass_udmp_del:" + obj->mtxt + "," + obj.get_deleter().delete_txt; }
8380

8481
// clang-format on
8582

0 commit comments

Comments
 (0)