Using dynamic_cast<AliasType> to determine pointee_depends_on_holder_owner. (#2910)

* Adaption of PyCLIF virtual_py_cpp_mix test.

* Removing ValueError: Ownership of instance with virtual overrides in Python cannot be transferred to C++. TODO: static_assert alias class needs to inherit from virtual_overrider_self_life_support.

* Bringing back ValueError: "... instance cannot safely be transferred to C++.", but based on dynamic_cast<AliasType>.

* Fixing oversight: adding test_class_sh_virtual_py_cpp_mix.cpp to cmake file.

* clang <= 3.6 compatibility.

* Fixing oversight: dynamic_raw_ptr_cast_if_possible needs special handling for To = void. Adding corresponding missing test in test_class_sh_virtual_py_cpp_mix. Moving dynamic_raw_ptr_cast_if_possible to separate header.

* Changing py::detail::virtual_overrider_self_life_support to py::virtual_overrider_self_life_support.
This commit is contained in:
Ralf W. Grosse-Kunstleve
2021-03-19 12:18:39 -07:00
committed by GitHub
parent 3f35af7441
commit 5319ca3817
12 changed files with 207 additions and 52 deletions

View File

@@ -106,6 +106,7 @@ set(PYBIND11_TEST_FILES
test_class_sh_inheritance.cpp
test_class_sh_trampoline_shared_ptr_cpp_arg.cpp
test_class_sh_unique_ptr_member.cpp
test_class_sh_virtual_py_cpp_mix.cpp
test_class_sh_with_alias.cpp
test_constants_and_functions.cpp
test_copy_move.cpp

View File

@@ -35,12 +35,14 @@ main_headers = {
"include/pybind11/smart_holder.h",
"include/pybind11/stl.h",
"include/pybind11/stl_bind.h",
"include/pybind11/virtual_overrider_self_life_support.h",
}
detail_headers = {
"include/pybind11/detail/class.h",
"include/pybind11/detail/common.h",
"include/pybind11/detail/descr.h",
"include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h",
"include/pybind11/detail/init.h",
"include/pybind11/detail/internals.h",
"include/pybind11/detail/smart_holder_poc.h",
@@ -48,7 +50,6 @@ detail_headers = {
"include/pybind11/detail/smart_holder_type_casters.h",
"include/pybind11/detail/type_caster_base.h",
"include/pybind11/detail/typeid.h",
"include/pybind11/detail/virtual_overrider_self_life_support.h",
}
cmake_files = {

View File

@@ -58,15 +58,10 @@ def test_shared_ptr_arg_identity():
del obj
pytest.gc_collect()
# python reference is still around since C++ has it
assert objref() is not None
assert tester.get_object() is objref()
assert tester.has_python_instance() is True
# python reference disappears once the C++ object releases it
tester.set_object(None)
pytest.gc_collect()
# SMART_HOLDER_WIP: the behavior below is DIFFERENT from PR #2839
# python reference is gone because it is not an Alias instance
assert objref() is None
assert tester.has_python_instance() is False
def test_shared_ptr_alias_nonpython():
@@ -90,7 +85,6 @@ def test_shared_ptr_alias_nonpython():
assert tester.has_instance() is True
assert tester.has_python_instance() is False
# SMART_HOLDER_WIP: the behavior below is DIFFERENT from PR #2839
# When we pass it as an arg to a new tester the python instance should
# disappear because it wasn't created with an alias
new_tester = m.SpBaseTester()
@@ -107,9 +101,9 @@ def test_shared_ptr_alias_nonpython():
# Gone!
assert tester.has_instance() is True
assert tester.has_python_instance() is True # False in PR #2839
assert tester.has_python_instance() is False
assert new_tester.has_instance() is True
assert new_tester.has_python_instance() is True # False in PR #2839
assert new_tester.has_python_instance() is False
def test_shared_ptr_goaway():

View File

@@ -0,0 +1,65 @@
#include "pybind11_tests.h"
#include <pybind11/smart_holder.h>
#include <memory>
namespace pybind11_tests {
namespace test_class_sh_virtual_py_cpp_mix {
class Base {
public:
virtual ~Base() = default;
virtual int get() const { return 101; }
// Some compilers complain about implicitly defined versions of some of the following:
Base() = default;
Base(const Base &) = default;
};
class CppDerivedPlain : public Base {
public:
int get() const override { return 202; }
};
class CppDerived : public Base {
public:
int get() const override { return 212; }
};
int get_from_cpp_plainc_ptr(const Base *b) { return b->get() + 4000; }
int get_from_cpp_unique_ptr(std::unique_ptr<Base> b) { return b->get() + 5000; }
struct BaseVirtualOverrider : Base, py::virtual_overrider_self_life_support {
using Base::Base;
int get() const override { PYBIND11_OVERRIDE(int, Base, get); }
};
struct CppDerivedVirtualOverrider : CppDerived, py::virtual_overrider_self_life_support {
using CppDerived::CppDerived;
int get() const override { PYBIND11_OVERRIDE(int, CppDerived, get); }
};
} // namespace test_class_sh_virtual_py_cpp_mix
} // namespace pybind11_tests
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::test_class_sh_virtual_py_cpp_mix::Base)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(
pybind11_tests::test_class_sh_virtual_py_cpp_mix::CppDerivedPlain)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::test_class_sh_virtual_py_cpp_mix::CppDerived)
TEST_SUBMODULE(class_sh_virtual_py_cpp_mix, m) {
using namespace pybind11_tests::test_class_sh_virtual_py_cpp_mix;
py::classh<Base, BaseVirtualOverrider>(m, "Base").def(py::init<>()).def("get", &Base::get);
py::classh<CppDerivedPlain, Base>(m, "CppDerivedPlain").def(py::init<>());
py::classh<CppDerived, Base, CppDerivedVirtualOverrider>(m, "CppDerived").def(py::init<>());
m.def("get_from_cpp_plainc_ptr", get_from_cpp_plainc_ptr, py::arg("b"));
m.def("get_from_cpp_unique_ptr", get_from_cpp_unique_ptr, py::arg("b"));
}

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
import pytest
from pybind11_tests import class_sh_virtual_py_cpp_mix as m
class PyBase(m.Base): # Avoiding name PyDerived, for more systematic naming.
def __init__(self):
m.Base.__init__(self)
def get(self):
return 323
class PyCppDerived(m.CppDerived):
def __init__(self):
m.CppDerived.__init__(self)
def get(self):
return 434
@pytest.mark.parametrize(
"ctor, expected",
[
(m.Base, 101),
(PyBase, 323),
(m.CppDerivedPlain, 202),
(m.CppDerived, 212),
(PyCppDerived, 434),
],
)
def test_base_get(ctor, expected):
obj = ctor()
assert obj.get() == expected
@pytest.mark.parametrize(
"ctor, expected",
[
(m.Base, 4101),
(PyBase, 4323),
(m.CppDerivedPlain, 4202),
(m.CppDerived, 4212),
(PyCppDerived, 4434),
],
)
def test_get_from_cpp_plainc_ptr(ctor, expected):
obj = ctor()
assert m.get_from_cpp_plainc_ptr(obj) == expected
@pytest.mark.parametrize(
"ctor, expected",
[
(m.Base, 5101),
(PyBase, 5323),
(m.CppDerivedPlain, 5202),
(m.CppDerived, 5212),
(PyCppDerived, 5434),
],
)
def test_get_from_cpp_unique_ptr(ctor, expected):
obj = ctor()
assert m.get_from_cpp_unique_ptr(obj) == expected

View File

@@ -35,7 +35,7 @@ struct AbaseAlias : Abase<SerNo> {
};
template <>
struct AbaseAlias<1> : Abase<1>, py::detail::virtual_overrider_self_life_support {
struct AbaseAlias<1> : Abase<1>, py::virtual_overrider_self_life_support {
using Abase<1>::Abase;
int Add(int other_val) const override {

View File

@@ -44,8 +44,9 @@ def test_drvd0_add_in_cpp_unique_ptr():
m.AddInCppUniquePtr(drvd, 0)
assert (
str(exc_info.value)
== "Ownership of instance with virtual overrides in Python"
" cannot be transferred to C++."
== "Alias class (also known as trampoline) does not inherit from"
" py::virtual_overrider_self_life_support, therefore the ownership of this"
" instance cannot safely be transferred to C++."
)
return # Comment out for manual leak checking (use `top` command).