Skip to content

Commit 3ab9655

Browse files
committed
fixup! solved multiple access issue
- Unregister instance after moving, thus avoiding reusing an invalidated instance pointer later on. Note: this implementation is rather hacky, because in hopes that the function clear_instance() inlined in detail/class.h will be available in cast.h as well. A clean solution should move the corresponding code into a shared header. Not sure also, I should clear_instance() or only deregister_instance()? - (Partially) reverts 8b45197 Moving the same variable twice into a function will error in C++ as well. We don't need to catch that in Python, do we?
1 parent 8b45197 commit 3ab9655

File tree

1 file changed

+28
-13
lines changed

1 file changed

+28
-13
lines changed

include/pybind11/cast.h

+28-13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
4040
PYBIND11_NAMESPACE_BEGIN(detail)
4141

42+
/// HACK: Declare inline function defined in class.h to be called in move_only_holder_caster's destructor
43+
/// Correct solution would be to move that function into a common header
44+
void clear_instance(PyObject *self);
45+
4246
/// A life support system for temporary objects created by `type_caster::load()`.
4347
/// Adding a patient will keep it alive up until the enclosing function returns.
4448
class loader_life_support {
@@ -588,9 +592,23 @@ class type_caster_generic {
588592

589593
// Base methods for generic caster; they are overridden in copyable_holder_caster
590594
void load_value(value_and_holder &&v_h) {
591-
value = v_h.value_ptr();
592-
if (value == nullptr)
593-
throw cast_error("Invalid object instance");
595+
auto *&vptr = v_h.value_ptr();
596+
// Lazy allocation for unallocated values:
597+
if (vptr == nullptr) {
598+
auto *type = v_h.type ? v_h.type : typeinfo;
599+
if (type->operator_new) {
600+
vptr = type->operator_new(type->type_size);
601+
} else {
602+
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
603+
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
604+
vptr = ::operator new(type->type_size,
605+
std::align_val_t(type->type_align));
606+
else
607+
#endif
608+
vptr = ::operator new(type->type_size);
609+
}
610+
}
611+
value = vptr;
594612
}
595613
bool try_implicit_casts(handle src, bool convert) {
596614
for (auto &cast : typeinfo->implicit_casts) {
@@ -1564,6 +1582,12 @@ struct move_only_holder_caster : public type_caster_base<type> {
15641582
using base::typeinfo;
15651583
using base::value;
15661584

1585+
~move_only_holder_caster() {
1586+
// if held object was actually moved, unregister it
1587+
if (!holder_helper<holder_type>::get(*holder_ptr))
1588+
clear_instance(reinterpret_cast<PyObject*>(v_h.inst));
1589+
}
1590+
15671591
bool load(handle& src, bool convert) {
15681592
bool success = base::template load_impl<move_only_holder_caster<type, holder_type>>(src, convert);
15691593
if (success) // On success, remember src pointer to withdraw later
@@ -1578,16 +1602,7 @@ struct move_only_holder_caster : public type_caster_base<type> {
15781602
#if !defined(__ICC) && !defined(__INTEL_COMPILER)
15791603
explicit
15801604
#endif
1581-
operator holder_type&&() {
1582-
// In load_value() value_ptr was still valid. If it's invalid now, another argument consumed the same value before.
1583-
if (!v_h.value_ptr())
1584-
throw cast_error("Multiple access to moved argument");
1585-
v_h.value_ptr() = nullptr;
1586-
// TODO: release object instance in python
1587-
// clear_instance(src_handle->ptr()); ???
1588-
1589-
return std::move(*holder_ptr);
1590-
}
1605+
operator holder_type&&() { return std::move(*holder_ptr); }
15911606

15921607
static handle cast(const holder_type &src, return_value_policy, handle) {
15931608
const auto *ptr = holder_helper<holder_type>::get(src);

0 commit comments

Comments
 (0)