mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-26 15:12:01 +00:00
Bug fix: trampoline_self_life_support CpCtor, MvCtor. (#2947)
This commit is contained in:
parent
6c922614ed
commit
8efd5e3820
@ -22,6 +22,8 @@ PYBIND11_NAMESPACE_END(detail)
|
|||||||
struct trampoline_self_life_support {
|
struct trampoline_self_life_support {
|
||||||
detail::value_and_holder v_h;
|
detail::value_and_holder v_h;
|
||||||
|
|
||||||
|
trampoline_self_life_support() = default;
|
||||||
|
|
||||||
void activate_life_support(const detail::value_and_holder &v_h_) {
|
void activate_life_support(const detail::value_and_holder &v_h_) {
|
||||||
Py_INCREF((PyObject *) v_h_.inst);
|
Py_INCREF((PyObject *) v_h_.inst);
|
||||||
v_h = v_h_;
|
v_h = v_h_;
|
||||||
@ -46,12 +48,14 @@ struct trampoline_self_life_support {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some compilers complain about implicitly defined versions of some of the following:
|
// For the next two, the default implementations generate undefined behavior (ASAN failures
|
||||||
trampoline_self_life_support() = default;
|
// manually verified). The reason is that v_h needs to be kept default-initialized.
|
||||||
trampoline_self_life_support(const trampoline_self_life_support &) = default;
|
trampoline_self_life_support(const trampoline_self_life_support &) {}
|
||||||
trampoline_self_life_support(trampoline_self_life_support &&) = default;
|
trampoline_self_life_support(trampoline_self_life_support &&) {}
|
||||||
trampoline_self_life_support &operator=(const trampoline_self_life_support &) = default;
|
|
||||||
trampoline_self_life_support &operator=(trampoline_self_life_support &&) = default;
|
// These should never be needed (please provide test cases if you think they are).
|
||||||
|
trampoline_self_life_support &operator=(const trampoline_self_life_support &) = delete;
|
||||||
|
trampoline_self_life_support &operator=(trampoline_self_life_support &&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||||
|
@ -107,6 +107,7 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_class_sh_factory_constructors.cpp
|
test_class_sh_factory_constructors.cpp
|
||||||
test_class_sh_inheritance.cpp
|
test_class_sh_inheritance.cpp
|
||||||
test_class_sh_trampoline_basic.cpp
|
test_class_sh_trampoline_basic.cpp
|
||||||
|
test_class_sh_trampoline_self_life_support.cpp
|
||||||
test_class_sh_trampoline_shared_ptr_cpp_arg.cpp
|
test_class_sh_trampoline_shared_ptr_cpp_arg.cpp
|
||||||
test_class_sh_trampoline_unique_ptr.cpp
|
test_class_sh_trampoline_unique_ptr.cpp
|
||||||
test_class_sh_unique_ptr_member.cpp
|
test_class_sh_unique_ptr_member.cpp
|
||||||
|
84
tests/test_class_sh_trampoline_self_life_support.cpp
Normal file
84
tests/test_class_sh_trampoline_self_life_support.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (c) 2021 The Pybind Development Team.
|
||||||
|
// All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "pybind11/smart_holder.h"
|
||||||
|
#include "pybind11/trampoline_self_life_support.h"
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct Big5 { // Also known as "rule of five".
|
||||||
|
std::string history;
|
||||||
|
|
||||||
|
explicit Big5(std::string history_start) : history{history_start} {}
|
||||||
|
|
||||||
|
Big5(const Big5 &other) { history = other.history + "_CpCtor"; }
|
||||||
|
|
||||||
|
Big5(Big5 &&other) { history = other.history + "_MvCtor"; }
|
||||||
|
|
||||||
|
Big5 &operator=(const Big5 &other) {
|
||||||
|
history = other.history + "_OpEqLv";
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Big5 &operator=(Big5 &&other) {
|
||||||
|
history = other.history + "_OpEqRv";
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Big5() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Big5() : history{"DefaultConstructor"} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Big5Trampoline : Big5, py::trampoline_self_life_support {
|
||||||
|
using Big5::Big5;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
PYBIND11_SMART_HOLDER_TYPE_CASTERS(Big5)
|
||||||
|
|
||||||
|
TEST_SUBMODULE(class_sh_trampoline_self_life_support, m) {
|
||||||
|
py::classh<Big5, Big5Trampoline>(m, "Big5")
|
||||||
|
.def(py::init<std::string>())
|
||||||
|
.def_readonly("history", &Big5::history);
|
||||||
|
|
||||||
|
m.def("action", [](std::unique_ptr<Big5> obj, int action_id) {
|
||||||
|
py::object o2 = py::none();
|
||||||
|
// This is very unusual, but needed to directly exercise the trampoline_self_life_support
|
||||||
|
// CpCtor, MvCtor, operator= lvalue, operator= rvalue.
|
||||||
|
auto obj_trampoline = dynamic_cast<Big5Trampoline *>(obj.get());
|
||||||
|
if (obj_trampoline != nullptr) {
|
||||||
|
switch (action_id) {
|
||||||
|
case 0: { // CpCtor
|
||||||
|
std::unique_ptr<Big5> cp(new Big5Trampoline(*obj_trampoline));
|
||||||
|
o2 = py::cast(std::move(cp));
|
||||||
|
} break;
|
||||||
|
case 1: { // MvCtor
|
||||||
|
std::unique_ptr<Big5> mv(new Big5Trampoline(std::move(*obj_trampoline)));
|
||||||
|
o2 = py::cast(std::move(mv));
|
||||||
|
} break;
|
||||||
|
case 2: { // operator= lvalue
|
||||||
|
std::unique_ptr<Big5> lv(new Big5Trampoline);
|
||||||
|
*lv = *obj_trampoline;
|
||||||
|
o2 = py::cast(std::move(lv));
|
||||||
|
} break;
|
||||||
|
case 3: { // operator= rvalue
|
||||||
|
std::unique_ptr<Big5> rv(new Big5Trampoline);
|
||||||
|
*rv = std::move(*obj_trampoline);
|
||||||
|
o2 = py::cast(std::move(rv));
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
py::object o1 = py::cast(std::move(obj));
|
||||||
|
return py::make_tuple(o1, o2);
|
||||||
|
});
|
||||||
|
}
|
37
tests/test_class_sh_trampoline_self_life_support.py
Normal file
37
tests/test_class_sh_trampoline_self_life_support.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import pybind11_tests.class_sh_trampoline_self_life_support as m
|
||||||
|
|
||||||
|
|
||||||
|
class PyBig5(m.Big5):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_m_big5():
|
||||||
|
obj = m.Big5("Seed")
|
||||||
|
assert obj.history == "Seed"
|
||||||
|
o1, o2 = m.action(obj, 0)
|
||||||
|
assert o1 is not obj
|
||||||
|
assert o1.history == "Seed"
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
obj.history
|
||||||
|
assert "Python instance was disowned" in str(excinfo.value)
|
||||||
|
assert o2 is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"action_id, expected_history",
|
||||||
|
[
|
||||||
|
(0, "Seed_CpCtor"),
|
||||||
|
(1, "Seed_MvCtor"),
|
||||||
|
(2, "Seed_OpEqLv"),
|
||||||
|
(3, "Seed_OpEqRv"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_py_big5(action_id, expected_history):
|
||||||
|
obj = PyBig5("Seed")
|
||||||
|
assert obj.history == "Seed"
|
||||||
|
o1, o2 = m.action(obj, action_id)
|
||||||
|
assert o1 is obj
|
||||||
|
assert o2.history == expected_history
|
Loading…
Reference in New Issue
Block a user