Skip to content

Commit faf1e61

Browse files
committed
Merge branch 'master' into weak_ptr_test_only
2 parents 0ecb396 + c630e22 commit faf1e61

7 files changed

+56
-87
lines changed

docs/advanced/classes.rst

+10-4
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,21 @@ helper class that is defined as follows:
8282
8383
The ``py::trampoline_self_life_support`` base class is needed to ensure
8484
that a ``std::unique_ptr`` can safely be passed between Python and C++. To
85-
steer clear of notorious pitfalls (e.g. inheritance slicing), it is best
86-
practice to always use the base class, in combination with
85+
help you steer clear of notorious pitfalls (e.g. inheritance slicing),
86+
pybind11 enforces that trampoline classes inherit from
87+
``py::trampoline_self_life_support`` if used in in combination with
8788
``py::smart_holder``.
8889

8990
.. note::
9091
For completeness, the base class has no effect if a holder other than
9192
``py::smart_holder`` used, including the default ``std::unique_ptr<T>``.
92-
Please think twice, though, the pitfalls are very real, and the overhead
93-
for using the safer ``py::smart_holder`` is very likely to be in the noise.
93+
To avoid confusion, pybind11 will fail to compile bindings that combine
94+
``py::trampoline_self_life_support`` with a holder other than
95+
``py::smart_holder``.
96+
97+
Please think twice, though, before deciding to not use the safer
98+
``py::smart_holder``. The pitfalls associated with avoiding it are very
99+
real, and the overhead for using it is very likely in the noise.
94100

95101
The macro :c:macro:`PYBIND11_OVERRIDE_PURE` should be used for pure virtual
96102
functions, and :c:macro:`PYBIND11_OVERRIDE` should be used for functions which have

include/pybind11/detail/type_caster_base.h

+2-5
Original file line numberDiff line numberDiff line change
@@ -829,11 +829,8 @@ struct load_helper : value_and_holder_helper {
829829

830830
auto *self_life_support
831831
= dynamic_raw_ptr_cast_if_possible<trampoline_self_life_support>(raw_type_ptr);
832-
if (self_life_support == nullptr && python_instance_is_alias) {
833-
throw value_error("Alias class (also known as trampoline) does not inherit from "
834-
"py::trampoline_self_life_support, therefore the ownership of this "
835-
"instance cannot safely be transferred to C++.");
836-
}
832+
// This is enforced indirectly by a static_assert in the class_ implementation:
833+
assert(!python_instance_is_alias || self_life_support);
837834

838835
std::unique_ptr<D> extracted_deleter = holder().template extract_deleter<T, D>(context);
839836

include/pybind11/pybind11.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "gil.h"
2121
#include "gil_safe_call_once.h"
2222
#include "options.h"
23+
#include "trampoline_self_life_support.h"
2324
#include "typing.h"
2425

2526
#include <cassert>
@@ -1982,7 +1983,21 @@ class class_ : public detail::generic_type {
19821983
"Unknown/invalid class_ template parameters provided");
19831984

19841985
static_assert(!has_alias || std::is_polymorphic<type>::value,
1985-
"Cannot use an alias class with a non-polymorphic type");
1986+
"Cannot use an alias class (aka trampoline) with a non-polymorphic type");
1987+
1988+
#ifndef PYBIND11_RUN_TESTING_WITH_SMART_HOLDER_AS_DEFAULT_BUT_NEVER_USE_IN_PRODUCTION_PLEASE
1989+
static_assert(!has_alias || !detail::is_smart_holder<holder_type>::value
1990+
|| std::is_base_of<trampoline_self_life_support, type_alias>::value,
1991+
"Alias class (aka trampoline) must inherit from"
1992+
" pybind11::trampoline_self_life_support if used in combination with"
1993+
" pybind11::smart_holder");
1994+
#endif
1995+
static_assert(!has_alias || detail::is_smart_holder<holder_type>::value
1996+
|| !std::is_base_of<trampoline_self_life_support, type_alias>::value,
1997+
"pybind11::trampoline_self_life_support is a smart_holder feature, therefore"
1998+
" an alias class (aka trampoline) should inherit from"
1999+
" pybind11::trampoline_self_life_support only if used in combination with"
2000+
" pybind11::smart_holder");
19862001

19872002
PYBIND11_OBJECT(class_, generic_type, PyType_Check)
19882003

tests/test_class_sh_factory_constructors.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ struct with_alias {
6464
with_alias &operator=(const with_alias &) = default;
6565
with_alias &operator=(with_alias &&) = default;
6666
};
67-
struct with_alias_alias : with_alias {};
67+
struct with_alias_alias : with_alias, py::trampoline_self_life_support {};
6868
struct sddwaa : std::default_delete<with_alias_alias> {};
6969

7070
} // namespace class_sh_factory_constructors

tests/test_class_sh_trampoline_basic.cpp

+16-41
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace pybind11_tests {
66
namespace class_sh_trampoline_basic {
77

8-
template <int SerNo> // Using int as a trick to easily generate a series of types.
98
struct Abase {
109
int val = 0;
1110
virtual ~Abase() = default;
@@ -20,63 +19,39 @@ struct Abase {
2019
Abase &operator=(Abase &&) noexcept = default;
2120
};
2221

23-
template <int SerNo>
24-
struct AbaseAlias : Abase<SerNo> {
25-
using Abase<SerNo>::Abase;
22+
struct AbaseAlias : Abase, py::trampoline_self_life_support {
23+
using Abase::Abase;
2624

2725
int Add(int other_val) const override {
28-
PYBIND11_OVERRIDE_PURE(int, /* Return type */
29-
Abase<SerNo>, /* Parent class */
30-
Add, /* Name of function in C++ (must match Python name) */
26+
PYBIND11_OVERRIDE_PURE(int, /* Return type */
27+
Abase, /* Parent class */
28+
Add, /* Name of function in C++ (must match Python name) */
3129
other_val);
3230
}
3331
};
3432

35-
template <>
36-
struct AbaseAlias<1> : Abase<1>, py::trampoline_self_life_support {
37-
using Abase<1>::Abase;
33+
int AddInCppRawPtr(const Abase *obj, int other_val) { return obj->Add(other_val) * 10 + 7; }
3834

39-
int Add(int other_val) const override {
40-
PYBIND11_OVERRIDE_PURE(int, /* Return type */
41-
Abase<1>, /* Parent class */
42-
Add, /* Name of function in C++ (must match Python name) */
43-
other_val);
44-
}
45-
};
46-
47-
template <int SerNo>
48-
int AddInCppRawPtr(const Abase<SerNo> *obj, int other_val) {
49-
return obj->Add(other_val) * 10 + 7;
50-
}
51-
52-
template <int SerNo>
53-
int AddInCppSharedPtr(std::shared_ptr<Abase<SerNo>> obj, int other_val) {
35+
int AddInCppSharedPtr(const std::shared_ptr<Abase> &obj, int other_val) {
5436
return obj->Add(other_val) * 100 + 11;
5537
}
5638

57-
template <int SerNo>
58-
int AddInCppUniquePtr(std::unique_ptr<Abase<SerNo>> obj, int other_val) {
39+
int AddInCppUniquePtr(std::unique_ptr<Abase> obj, int other_val) {
5940
return obj->Add(other_val) * 100 + 13;
6041
}
6142

62-
template <int SerNo>
63-
void wrap(py::module_ m, const char *py_class_name) {
64-
py::classh<Abase<SerNo>, AbaseAlias<SerNo>>(m, py_class_name)
65-
.def(py::init<int>(), py::arg("val"))
66-
.def("Get", &Abase<SerNo>::Get)
67-
.def("Add", &Abase<SerNo>::Add, py::arg("other_val"));
68-
69-
m.def("AddInCppRawPtr", AddInCppRawPtr<SerNo>, py::arg("obj"), py::arg("other_val"));
70-
m.def("AddInCppSharedPtr", AddInCppSharedPtr<SerNo>, py::arg("obj"), py::arg("other_val"));
71-
m.def("AddInCppUniquePtr", AddInCppUniquePtr<SerNo>, py::arg("obj"), py::arg("other_val"));
72-
}
73-
7443
} // namespace class_sh_trampoline_basic
7544
} // namespace pybind11_tests
7645

7746
using namespace pybind11_tests::class_sh_trampoline_basic;
7847

7948
TEST_SUBMODULE(class_sh_trampoline_basic, m) {
80-
wrap<0>(m, "Abase0");
81-
wrap<1>(m, "Abase1");
49+
py::classh<Abase, AbaseAlias>(m, "Abase")
50+
.def(py::init<int>(), py::arg("val"))
51+
.def("Get", &Abase::Get)
52+
.def("Add", &Abase::Add, py::arg("other_val"));
53+
54+
m.def("AddInCppRawPtr", AddInCppRawPtr, py::arg("obj"), py::arg("other_val"));
55+
m.def("AddInCppSharedPtr", AddInCppSharedPtr, py::arg("obj"), py::arg("other_val"));
56+
m.def("AddInCppUniquePtr", AddInCppUniquePtr, py::arg("obj"), py::arg("other_val"));
8257
}
+10-34
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,35 @@
11
from __future__ import annotations
22

3-
import pytest
4-
53
from pybind11_tests import class_sh_trampoline_basic as m
64

75

8-
class PyDrvd0(m.Abase0):
6+
class PyDrvd(m.Abase):
97
def __init__(self, val):
108
super().__init__(val)
119

1210
def Add(self, other_val):
1311
return self.Get() * 100 + other_val
1412

1513

16-
class PyDrvd1(m.Abase1):
17-
def __init__(self, val):
18-
super().__init__(val)
19-
20-
def Add(self, other_val):
21-
return self.Get() * 200 + other_val
22-
23-
24-
def test_drvd0_add():
25-
drvd = PyDrvd0(74)
14+
def test_drvd_add():
15+
drvd = PyDrvd(74)
2616
assert drvd.Add(38) == (74 * 10 + 3) * 100 + 38
2717

2818

29-
def test_drvd0_add_in_cpp_raw_ptr():
30-
drvd = PyDrvd0(52)
19+
def test_drvd_add_in_cpp_raw_ptr():
20+
drvd = PyDrvd(52)
3121
assert m.AddInCppRawPtr(drvd, 27) == ((52 * 10 + 3) * 100 + 27) * 10 + 7
3222

3323

34-
def test_drvd0_add_in_cpp_shared_ptr():
24+
def test_drvd_add_in_cpp_shared_ptr():
3525
while True:
36-
drvd = PyDrvd0(36)
26+
drvd = PyDrvd(36)
3727
assert m.AddInCppSharedPtr(drvd, 56) == ((36 * 10 + 3) * 100 + 56) * 100 + 11
3828
return # Comment out for manual leak checking (use `top` command).
3929

4030

41-
def test_drvd0_add_in_cpp_unique_ptr():
42-
while True:
43-
drvd = PyDrvd0(0)
44-
with pytest.raises(ValueError) as exc_info:
45-
m.AddInCppUniquePtr(drvd, 0)
46-
assert (
47-
str(exc_info.value)
48-
== "Alias class (also known as trampoline) does not inherit from"
49-
" py::trampoline_self_life_support, therefore the ownership of this"
50-
" instance cannot safely be transferred to C++."
51-
)
52-
return # Comment out for manual leak checking (use `top` command).
53-
54-
55-
def test_drvd1_add_in_cpp_unique_ptr():
31+
def test_drvd_add_in_cpp_unique_ptr():
5632
while True:
57-
drvd = PyDrvd1(25)
58-
assert m.AddInCppUniquePtr(drvd, 83) == ((25 * 10 + 3) * 200 + 83) * 100 + 13
33+
drvd = PyDrvd(25)
34+
assert m.AddInCppUniquePtr(drvd, 83) == ((25 * 10 + 3) * 100 + 83) * 100 + 13
5935
return # Comment out for manual leak checking (use `top` command).

tests/test_class_sh_trampoline_shared_ptr_cpp_arg.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct SpBase {
2828

2929
std::shared_ptr<SpBase> pass_through_shd_ptr(const std::shared_ptr<SpBase> &obj) { return obj; }
3030

31-
struct PySpBase : SpBase {
31+
struct PySpBase : SpBase, py::trampoline_self_life_support {
3232
using SpBase::SpBase;
3333
bool is_base_used() override { PYBIND11_OVERRIDE(bool, SpBase, is_base_used); }
3434
};

0 commit comments

Comments
 (0)