Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[smart_holder] Bake smart_holder functionality into class_ and type_caster_base #5257

Merged
merged 19 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
82c9ce7
Put bakein branch @ 18b72c0ffa6ff2747ed6c4b869a80adfb8e762c9 on top o…
Jul 21, 2024
fbd1295
Add back README_smart_holder.rst in tests/extra_python_package/test_f…
Jul 21, 2024
58f9e18
Merge branch 'smart_holder' into bakein_sh
Jul 22, 2024
c02d2cd
Restore smart_holder_poc.h as-is on smart_holder branch (i.e. undo `P…
Jul 23, 2024
99b6572
Insert `std::move()` as suggested by @laramiel
Jul 26, 2024
16cf7ad
`property_cpp_function_sh_*` named specializations, as suggested by @…
Jul 26, 2024
0bcfbd4
Call `property_cpp_function_classic` member functions, rather than in…
Jul 26, 2024
2644d6e
Use `PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT` in holder_com…
Jul 26, 2024
336860d
Systematically rename `loaded_as` to `load_as` (`shared_ptr`, `unique…
Jul 26, 2024
cb6a85f
Make change as suggested by @laramiel. This makes it much more obviou…
Jul 26, 2024
0316f74
Resolve `BAKEIN_WIP: Rewrite comment.` for `property_cpp_function_*` …
Jul 27, 2024
053467f
Resolve `BAKEIN_WIP: Add comment to explain: This is meant for stress…
Jul 27, 2024
523eafa
Resolve all remaining BAKEIN_WIP (in pybind11/cast.h).
Jul 27, 2024
7c0aed7
Remove obsolete `using holder_type = smart_holder;` in `load_helper`
Jul 27, 2024
0ca3ca7
Add SMART_HOLDER_BAKEIN_FOLLOW_ON comment for `internals::default_hol…
Jul 27, 2024
4a7f895
README_smart_holder.rst update (line count reduced from 356 to 123).
Jul 27, 2024
67ac541
Merge branch 'sh_merge_master' into bakein_sh_merge_ahead
Jul 29, 2024
8ffe837
Merge branch 'sh_merge_master' into bakein_sh
Jul 30, 2024
2e9769a
Merge branch 'smart_holder' into bakein_sh
Jul 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,9 @@ set(PYBIND11_HEADERS
include/pybind11/detail/init.h
include/pybind11/detail/internals.h
include/pybind11/detail/smart_holder_poc.h
include/pybind11/detail/smart_holder_sfinae_hooks_only.h
include/pybind11/detail/smart_holder_type_casters.h
include/pybind11/detail/type_caster_base.h
include/pybind11/detail/typeid.h
include/pybind11/detail/using_smart_holder.h
include/pybind11/detail/value_and_holder.h
include/pybind11/attr.h
include/pybind11/buffer_info.h
Expand Down
4 changes: 4 additions & 0 deletions include/pybind11/attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ struct type_record {
/// Is the class inheritable from python classes?
bool is_final : 1;

#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
holder_enum_t holder_enum_v = holder_enum_t::undefined;
#endif

PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
auto *base_info = detail::get_type_info(base, false);
if (!base_info) {
Expand Down
294 changes: 262 additions & 32 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

#include "detail/common.h"
#include "detail/descr.h"
#include "detail/smart_holder_sfinae_hooks_only.h"
#include "detail/type_caster_base.h"
#include "detail/typeid.h"
#include "pytypes.h"
Expand All @@ -29,33 +28,17 @@
#include <utility>
#include <vector>

#ifdef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
# include "detail/smart_holder_type_casters.h"
#endif

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)

PYBIND11_WARNING_DISABLE_MSVC(4127)

PYBIND11_NAMESPACE_BEGIN(detail)

#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
template <typename T>
class type_caster_for_class_ : public type_caster_base<T> {};
#endif

template <typename type, typename SFINAE = void>
class type_caster : public type_caster_for_class_<type> {};

class type_caster : public type_caster_base<type> {};
template <typename type>
using make_caster = type_caster<intrinsic_t<type>>;

template <typename T>
struct type_uses_smart_holder_type_caster {
static constexpr bool value
= std::is_base_of<smart_holder_type_caster_base_tag, make_caster<T>>::value;
};

// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
template <typename T>
typename make_caster<T>::template cast_op_type<T> cast_op(make_caster<T> &caster) {
Expand Down Expand Up @@ -852,11 +835,154 @@ struct copyable_holder_caster : public type_caster_base<type> {
holder_type holder;
};

#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT

template <typename, typename SFINAE = void>
struct copyable_holder_caster_shared_ptr_with_smart_holder_support_enabled : std::true_type {};

// BAKEIN_WIP
template <typename type>
struct copyable_holder_caster<
type,
std::shared_ptr<type>,
enable_if_t<copyable_holder_caster_shared_ptr_with_smart_holder_support_enabled<type>::value>>
: public type_caster_base<type> {
public:
using base = type_caster_base<type>;
static_assert(std::is_base_of<base, type_caster<type>>::value,
"Holder classes are only supported for custom types");
using base::base;
using base::cast;
using base::typeinfo;
using base::value;

bool load(handle src, bool convert) {
return base::template load_impl<copyable_holder_caster<type, std::shared_ptr<type>>>(
src, convert);
}

explicit operator type *() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
throw std::runtime_error("BAKEIN_WIP: operator type *() shared_ptr");
}
return this->value;
}

explicit operator type &() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
throw std::runtime_error("BAKEIN_WIP: operator type &() shared_ptr");
}
// static_cast works around compiler error with MSVC 17 and CUDA 10.2
// see issue #2180
return *(static_cast<type *>(this->value));
}

explicit operator std::shared_ptr<type> *() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
throw std::runtime_error("BAKEIN_WIP: operator std::shared_ptr<type> *()");
}
return std::addressof(shared_ptr_holder);
}

explicit operator std::shared_ptr<type> &() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
shared_ptr_holder = sh_load_helper.loaded_as_shared_ptr(value);
}
return shared_ptr_holder;
}

static handle
cast(const std::shared_ptr<type> &src, return_value_policy policy, handle parent) {
const auto *ptr = src.get();
auto st = type_caster_base<type>::src_and_type(ptr);
if (st.second == nullptr) {
return handle(); // no type info: error will be set already
}
if (st.second->holder_enum_v == detail::holder_enum_t::smart_holder) {
return smart_holder_type_caster_support::smart_holder_from_shared_ptr(
src, policy, parent, st);
}
return type_caster_base<type>::cast_holder(ptr, &src);
}

// This function will succeed even if the `responsible_parent` does not own the
// wrapped C++ object directly.
// It is the responsibility of the caller to ensure that the `responsible_parent`
// has a `keep_alive` relationship with the owner of the wrapped C++ object, or
// that the wrapped C++ object lives for the duration of the process.
static std::shared_ptr<type> shared_ptr_with_responsible_parent(handle responsible_parent) {
copyable_holder_caster loader;
loader.load(responsible_parent, /*convert=*/false);
assert(loader.typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder);
return loader.sh_load_helper.loaded_as_shared_ptr(loader.value, responsible_parent);
}

protected:
friend class type_caster_generic;
void check_holder_compat() {
if (typeinfo->default_holder) {
throw cast_error("Unable to load a custom holder type from a default-holder instance");
}
}

void load_value(value_and_holder &&v_h) {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
sh_load_helper.loaded_v_h = v_h;
value = sh_load_helper.get_void_ptr_or_nullptr();
return;
}
if (v_h.holder_constructed()) {
value = v_h.value_ptr();
shared_ptr_holder = v_h.template holder<std::shared_ptr<type>>();
return;
}
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
# if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
"type information)");
# else
"of type '"
+ type_id<std::shared_ptr<type>>() + "''");
# endif
}

template <typename T = std::shared_ptr<type>,
detail::enable_if_t<!std::is_constructible<T, const T &, type *>::value, int> = 0>
bool try_implicit_casts(handle, bool) {
return false;
}

template <typename T = std::shared_ptr<type>,
detail::enable_if_t<std::is_constructible<T, const T &, type *>::value, int> = 0>
bool try_implicit_casts(handle src, bool convert) {
for (auto &cast : typeinfo->implicit_casts) {
copyable_holder_caster sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
value = cast.second(sub_caster.value);
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
// BAKEIN_WIP: Copy pointer only?
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
} else {
shared_ptr_holder
= std::shared_ptr<type>(sub_caster.shared_ptr_holder, (type *) value);
}
return true;
}
}
return false;
}

static bool try_direct_conversions(handle) { return false; }

std::shared_ptr<type> shared_ptr_holder;
smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl
};

#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT

/// Specialize for the common std::shared_ptr, so users don't need to
template <typename T>
class type_caster<std::shared_ptr<T>> : public copyable_holder_caster<T, std::shared_ptr<T>> {};
#endif

/// Type caster for holder types like std::unique_ptr.
/// Please consider the SFINAE hook an implementation detail, as explained
Expand All @@ -873,11 +999,114 @@ struct move_only_holder_caster {
static constexpr auto name = type_caster_base<type>::name;
};

#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT

template <typename, typename SFINAE = void>
struct move_only_holder_caster_unique_ptr_with_smart_holder_support_enabled : std::true_type {};

// BAKEIN_WIP
template <typename type, typename deleter>
struct move_only_holder_caster<
type,
std::unique_ptr<type, deleter>,
enable_if_t<move_only_holder_caster_unique_ptr_with_smart_holder_support_enabled<type>::value>>
: public type_caster_base<type> {
public:
using base = type_caster_base<type>;
static_assert(std::is_base_of<base, type_caster<type>>::value,
"Holder classes are only supported for custom types");
using base::base;
using base::cast;
using base::typeinfo;
using base::value;

static handle
cast(std::unique_ptr<type, deleter> &&src, return_value_policy policy, handle parent) {
auto *ptr = src.get();
auto st = type_caster_base<type>::src_and_type(ptr);
if (st.second == nullptr) {
return handle(); // no type info: error will be set already
}
if (st.second->holder_enum_v == detail::holder_enum_t::smart_holder) {
return smart_holder_type_caster_support::smart_holder_from_unique_ptr(
std::move(src), policy, parent, st);
}
return type_caster_generic::cast(st.first,
return_value_policy::take_ownership,
{},
st.second,
nullptr,
nullptr,
std::addressof(src));
}

static handle
cast(const std::unique_ptr<type, deleter> &src, return_value_policy policy, handle parent) {
if (!src) {
return none().release();
}
if (policy == return_value_policy::automatic) {
policy = return_value_policy::reference_internal;
}
if (policy != return_value_policy::reference_internal) {
throw cast_error("Invalid return_value_policy for unique_ptr&");
}
return type_caster_base<type>::cast(src.get(), policy, parent);
}

bool load(handle src, bool convert) {
return base::template load_impl<
move_only_holder_caster<type, std::unique_ptr<type, deleter>>>(src, convert);
}

void load_value(value_and_holder &&v_h) {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
sh_load_helper.loaded_v_h = v_h;
sh_load_helper.loaded_v_h.type = typeinfo;
value = sh_load_helper.get_void_ptr_or_nullptr();
return;
}
throw std::runtime_error("BAKEIN_WIP: What is the best behavior here (load_value)?");
}

template <typename>
using cast_op_type = std::unique_ptr<type, deleter>;

explicit operator std::unique_ptr<type, deleter>() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
return sh_load_helper.template loaded_as_unique_ptr<deleter>(value);
}
pybind11_fail("Passing std::unique_ptr from Python to C++ requires smart_holder.");
}

bool try_implicit_casts(handle src, bool convert) {
for (auto &cast : typeinfo->implicit_casts) {
move_only_holder_caster sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
value = cast.second(sub_caster.value);
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
// BAKEIN_WIP: Copy pointer only?
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
} else {
throw std::runtime_error(
"BAKEIN_WIP: What is the best behavior here (try_implicit_casts)?");
}
return true;
}
}
return false;
}

static bool try_direct_conversions(handle) { return false; }

smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl
};

#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT

template <typename type, typename deleter>
class type_caster<std::unique_ptr<type, deleter>>
: public move_only_holder_caster<type, std::unique_ptr<type, deleter>> {};
#endif

template <typename type, typename holder_type>
using type_caster_holder = conditional_t<is_copy_constructible<holder_type>::value,
Expand Down Expand Up @@ -906,10 +1135,16 @@ struct always_construct_holder {
template <typename base, typename holder>
struct is_holder_type
: std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>> {};
// Specialization for always-supported unique_ptr holders:

// Specializations for always-supported holders:
template <typename base, typename deleter>
struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {};

#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
template <typename base>
struct is_holder_type<base, smart_holder> : std::true_type {};
#endif

#ifdef PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION // See PR #4888

// This leads to compilation errors if a specialization is missing.
Expand Down Expand Up @@ -1141,7 +1376,6 @@ template <typename type>
using cast_is_temporary_value_reference
= bool_constant<(std::is_reference<type>::value || std::is_pointer<type>::value)
&& !std::is_base_of<type_caster_generic, make_caster<type>>::value
&& !type_uses_smart_holder_type_caster<intrinsic_t<type>>::value
&& !std::is_same<intrinsic_t<type>, void>::value>;

// When a value returned from a C++ function is being cast back to Python, we almost always want to
Expand All @@ -1155,9 +1389,7 @@ struct return_value_policy_override {
template <typename Return>
struct return_value_policy_override<
Return,
detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value
|| type_uses_smart_holder_type_caster<intrinsic_t<Return>>::value,
void>> {
detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value, void>> {
static return_value_policy policy(return_value_policy p) {
return !std::is_lvalue_reference<Return>::value && !std::is_pointer<Return>::value
? return_value_policy::move
Expand Down Expand Up @@ -1857,10 +2089,8 @@ PYBIND11_NAMESPACE_END(detail)

template <typename T>
handle type::handle_of() {
static_assert(
detail::any_of<std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>,
detail::type_uses_smart_holder_type_caster<T>>::value,
"py::type::of<T> only supports the case where T is a registered C++ types.");
static_assert(std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>::value,
"py::type::of<T> only supports the case where T is a registered C++ types.");

return detail::get_type_handle(typeid(T), true);
}
Expand All @@ -1869,7 +2099,7 @@ handle type::handle_of() {
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \
namespace detail { \
template <> \
class type_caster<__VA_ARGS__> : public type_caster_for_class_<__VA_ARGS__> {}; \
class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> {}; \
} \
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

Expand Down
Loading