Adding smart_holder_type_casters for unique_ptr with custom deleter. SEVERE CODE DUPLICATION. This commit is to establish a baseline for consolidating the unique_ptr code.

This commit is contained in:
Ralf W. Grosse-Kunstleve 2021-02-01 14:06:58 -08:00
parent 231bd84fa0
commit a9bfcbdcc3
3 changed files with 120 additions and 0 deletions

View File

@ -318,6 +318,29 @@ struct smart_holder_type_caster_load {
return result;
}
template <typename D>
std::unique_ptr<T, D> loaded_as_unique_ptr_with_deleter() {
holder().template ensure_compatible_rtti_uqp_del<D>("loaded_as_unique_ptr_with_deleter");
holder().ensure_use_count_1("loaded_as_unique_ptr_with_deleter");
auto raw_void_ptr = holder().template as_raw_ptr_unowned<void>();
// MISSING: Safety checks for type conversions
// (T must be polymorphic or meet certain other conditions).
T *raw_type_ptr = convert_type(raw_void_ptr);
// Critical transfer-of-ownership section. This must stay together.
holder().release_ownership();
auto result = std::unique_ptr<T, D>(raw_type_ptr);
void *value_void_ptr
= load_impl.loaded_v_h.value_ptr(); // Expected to be identical to raw_void_ptr.
load_impl.loaded_v_h.holder<holder_type>().~holder_type();
load_impl.loaded_v_h.set_holder_constructed(false);
load_impl.loaded_v_h.value_ptr() = nullptr;
deregister_instance(load_impl.loaded_v_h.inst, value_void_ptr, load_impl.loaded_v_h.type);
return result;
}
private:
modified_type_caster_generic_load_impl load_impl;
@ -622,6 +645,72 @@ struct smart_holder_type_caster<std::unique_ptr<T const>> : smart_holder_type_ca
operator std::unique_ptr<T const>() { return this->loaded_as_unique_ptr(); }
};
template <typename T, typename D>
struct smart_holder_type_caster<std::unique_ptr<T, D>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::unique_ptr<T, D>>();
static handle cast(std::unique_ptr<T, D> &&src, return_value_policy policy, handle parent) {
if (policy != return_value_policy::automatic
&& policy != return_value_policy::reference_internal) {
// IMPROVABLE: Error message.
throw cast_error("Invalid return_value_policy for unique_ptr.");
}
auto src_raw_ptr = src.get();
auto st = type_caster_base<T>::src_and_type(src_raw_ptr);
if (st.first == nullptr)
return none().release(); // PyErr was set already.
void *src_raw_void_ptr = static_cast<void *>(src_raw_ptr);
const detail::type_info *tinfo = st.second;
if (find_registered_python_instance(src_raw_void_ptr, tinfo))
throw cast_error("Invalid unique_ptr: another instance owns this pointer already.");
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
inst_raw_ptr->owned = true;
void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr();
valueptr = src_raw_void_ptr;
auto smhldr = pybindit::memory::smart_holder::from_unique_ptr_with_deleter(std::move(src));
tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));
if (policy == return_value_policy::reference_internal)
keep_alive_impl(inst, parent);
return inst.release();
}
template <typename>
using cast_op_type = std::unique_ptr<T, D>;
operator std::unique_ptr<T, D>() {
return this->template loaded_as_unique_ptr_with_deleter<D>();
}
};
template <typename T, typename D>
struct smart_holder_type_caster<std::unique_ptr<T const, D>>
: smart_holder_type_caster_load<T>, smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::unique_ptr<T const, D>>();
static handle
cast(std::unique_ptr<T const, D> &&src, return_value_policy policy, handle parent) {
return smart_holder_type_caster<std::unique_ptr<T, D>>::cast(
std::unique_ptr<T, D>(const_cast<T *>(src.release())), // Const2Mutbl
policy,
parent);
}
template <typename>
using cast_op_type = std::unique_ptr<T const, D>;
operator std::unique_ptr<T const, D>() {
return this->template loaded_as_unique_ptr_with_deleter<D>();
}
};
#define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T) \
namespace pybind11 { \
namespace detail { \
@ -639,6 +728,12 @@ struct smart_holder_type_caster<std::unique_ptr<T const>> : smart_holder_type_ca
template <> \
class type_caster<std::unique_ptr<T const>> \
: public smart_holder_type_caster<std::unique_ptr<T const>> {}; \
template <typename D> \
class type_caster<std::unique_ptr<T, D>> \
: public smart_holder_type_caster<std::unique_ptr<T, D>> {}; \
template <typename D> \
class type_caster<std::unique_ptr<T const, D>> \
: public smart_holder_type_caster<std::unique_ptr<T const, D>> {}; \
} \
}

View File

@ -40,6 +40,15 @@ std::unique_ptr<atyp const> rtrn_uqcp_atyp() { return std::unique_ptr<atyp const
std::string pass_uqmp_atyp(std::unique_ptr<atyp > obj) { return "pass_uqmp:" + obj->mtxt; }
std::string pass_uqcp_atyp(std::unique_ptr<atyp const> obj) { return "pass_uqcp:" + obj->mtxt; }
struct uqmd : std::default_delete<atyp > {};
struct uqcd : std::default_delete<atyp const> {};
std::unique_ptr<atyp, uqmd> rtrn_uqmp_del_atyp() { return std::unique_ptr<atyp, uqmd>(new atyp{"rtrn_uqmp_del"}); }
std::unique_ptr<atyp const, uqcd> rtrn_uqcp_del_atyp() { return std::unique_ptr<atyp const, uqcd>(new atyp{"rtrn_uqcp_del"}); }
std::string pass_uqmp_del_atyp(std::unique_ptr<atyp, uqmd> obj) { return "pass_uqmp_del:" + obj->mtxt; }
std::string pass_uqcp_del_atyp(std::unique_ptr<atyp const, uqcd> obj) { return "pass_uqcp_del:" + obj->mtxt; }
// clang-format on
// Helpers for testing.
@ -89,6 +98,12 @@ TEST_SUBMODULE(class_sh_basic, m) {
m.def("pass_uqmp_atyp", pass_uqmp_atyp);
m.def("pass_uqcp_atyp", pass_uqcp_atyp);
m.def("rtrn_uqmp_del_atyp", rtrn_uqmp_del_atyp);
m.def("rtrn_uqcp_del_atyp", rtrn_uqcp_del_atyp);
m.def("pass_uqmp_del_atyp", pass_uqmp_del_atyp);
m.def("pass_uqcp_del_atyp", pass_uqcp_del_atyp);
// Helpers for testing.
// These require selected functions above to work first, as indicated:
m.def("get_mtxt", get_mtxt); // pass_cref_atyp

View File

@ -51,6 +51,16 @@ def test_load_unique_ptr():
assert m.pass_uqcp_atyp(m.atyp("Uqcp")) == "pass_uqcp:Uqcp"
def test_cast_unique_ptr_with_deleter():
assert m.get_mtxt(m.rtrn_uqmp_del_atyp()) == "rtrn_uqmp_del"
assert m.get_mtxt(m.rtrn_uqcp_del_atyp()) == "rtrn_uqcp_del"
def test_load_unique_ptr_with_deleter():
assert m.pass_uqmp_del_atyp(m.rtrn_uqmp_del_atyp()) == "pass_uqmp_del:rtrn_uqmp_del"
assert m.pass_uqcp_del_atyp(m.rtrn_uqcp_del_atyp()) == "pass_uqcp_del:rtrn_uqcp_del"
@pytest.mark.parametrize(
"pass_atyp, argm, rtrn",
[