squash-merge smart_holder branch into master (#5542)

* Pure `git merge --squash smart_holder` (no manual interventions).

* Remove ubench/ directory.

* Remove include/pybind11/smart_holder.h

* [ci skip] smart_ptrs.rst updates [WIP/unfinished]

* [ci skip] smart_ptrs.rst updates continued; also updating classes.rst, advanced/classes.rst

* Remove README_smart_holder.rst

* Restore original README.rst from master

* [ci skip] Minimal change to README.rst, to leave a hint that this is pybind11v3

* [ci skip] Work in ChatGPT suggestions.

* Change macro name to PYBIND11_RUN_TESTING_WITH_SMART_HOLDER_AS_DEFAULT_BUT_NEVER_USE_IN_PRODUCTION_PLEASE

* Add a note pointing to the holder reinterpret_cast.

* Incorporate suggestion by @virtuald: https://github.com/pybind/pybind11/pull/5542#discussion_r1967000989

* Systematically change most py::class_ to py::classh under docs/

* Remove references to README_smart_holder.rst

This should have been part of commit eb550d03d3.

* [ci skip] Fix minor oversight (``class_`` -> ``py::class_``) noticed by chance.

* [ci skip] Resolve suggestion by @virtuald

https://github.com/pybind/pybind11/pull/5542#discussion_r1969940605

* [ci skip] Apply suggestions by @timohl (thanks!)

* https://github.com/pybind/pybind11/pull/5542#discussion_r1970714551
* https://github.com/pybind/pybind11/pull/5542#discussion_r1971315329
* https://github.com/pybind/pybind11/pull/5542#discussion_r1971322821

* Replace `classh : class_` inhertance with `using`, as suggested by @henryiii

https://github.com/pybind/pybind11/pull/5542#issuecomment-2689034104

* Revert "Systematically change most py::class_ to py::classh under docs/"

This reverts commit ac9d31e13f.

* docs: focus on py::smart_holder instead of py::classh

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Restore minor general fixes that got lost when ac9d31e13f was reverted.

* Remove `- smart_holder` from list of branches in all .github/workflows

* Extend classh note to explain whitespace noise motivation.

* Suggest `py::smart_holder` for "most situations for safety"

* Add back PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT

This define was
* introduced with https://github.com/pybind/pybind11/pull/5286
* removed with https://github.com/pybind/pybind11/pull/5531

It is has been in use here:
* https://github.com/pybind/pybind11_protobuf/blob/f02a2b7653bc50eb5119d125842a3870db95d251/pybind11_protobuf/native_proto_caster.h#L89-L101

Currently pybind11 unit tests for the two holder caster backwards compatibility traits

* `copyable_holder_caster_shared_ptr_with_smart_holder_support_enabled`
* `move_only_holder_caster_unique_ptr_with_smart_holder_support_enabled`

are missing.

* Add py::trampoline_self_life_support to all trampoline examples under docs/.

Address suggestion by @timohl:

* https://github.com/pybind/pybind11/pull/5542#issuecomment-2686452062

Add to the "please think twice" note: the overhead for safety is likely in the noise.

Also fix a two-fold inconsistency introduced by revert-commit 1e646c91b4:

1.

py::trampoline_self_life_support is mentioned in a note, but is missing in the example right before.

2.

The section starting with

    To enable safely passing a ``std::unique_ptr`` to a trampoline object between

is obsolete.

* Fix whitespace accident (indentation) introduced with 1e646c91b4

Apparently the mis-indentation was introduced when resolving merge conflicts for what became 1e646c91b4

* WHITESPACE CHANGES ONLY in README.rst (list of people that made significant contributions)

* Add Ethan Steinberg to list of people that made significant contributions (for completeness, unrelated to smart_holder work).

* [ci skip] Add to list of people that made significant contributions: major and/or influential contributors to smart_holder branch

* #2904 by @rhaschke was merged on Mar 16, 2021
* #3012 by @rhaschke was merged on May 28, 2021
* #3039 by @jakobandersen was merged on Jun 29, 2021
* #3048 by @Skylion007 was merged on Jun 18, 2021
* #3588 by @virtuald was merged on Jan 3, 2022
* #3633 by @wangxf123456 was merged on Jan 25, 2022
* #3635 by @virtuald was merged on Jan 26, 2022
* #3645 by @wangxf123456 was merged on Jan 25, 2022
* #3796 by @wangxf123456 was merged on Mar 10, 2022
* #3807 by @wangxf123456 was merged on Mar 18, 2022
* #3838 by @wangxf123456 was merged on Apr 15, 2022
* #3929 by @tomba was merged on May 7, 2022
* #4031 by @wangxf123456 was merged on Jun 27, 2022
* #4343 by @wangxf123456 was merged on Nov 18, 2022
* #4381 by @wangxf123456 was merged on Dec 5, 2022
* #4539 by @wangxf123456 was merged on Feb 28, 2023
* #4609 by @wangxf123456 was merged on Apr 6, 2023
* #4775 by @wangxf123456 was merged on Aug 3, 2023
* #4921 by @iwanders was merged on Nov 7, 2023
* #4924 by @iwanders was merged on Nov 6, 2023
* #5401 by @msimacek was merged on Oct 8, 2024

Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com>
Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com>
Co-authored-by: Ivor Wanders <iwanders@users.noreply.github.com>
Co-authored-by: Jakob Lykke Andersen <Jakob@caput.dk>
Co-authored-by: Michael Šimáček <michael.simacek@oracle.com>
Co-authored-by: Robert Haschke <rhaschke@users.noreply.github.com>
Co-authored-by: Tomi Valkeinen <tomi.valkeinen@iki.fi>
Co-authored-by: Xiaofei Wang <6218006+wangxf123456@users.noreply.github.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com>
Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com>
Co-authored-by: Ivor Wanders <iwanders@users.noreply.github.com>
Co-authored-by: Jakob Lykke Andersen <Jakob@caput.dk>
Co-authored-by: Michael Šimáček <michael.simacek@oracle.com>
Co-authored-by: Robert Haschke <rhaschke@users.noreply.github.com>
Co-authored-by: Tomi Valkeinen <tomi.valkeinen@iki.fi>
Co-authored-by: Xiaofei Wang <6218006+wangxf123456@users.noreply.github.com>
This commit is contained in:
Ralf W. Grosse-Kunstleve
2025-03-05 12:40:53 -08:00
committed by GitHub
co-authored by Aaron Gokaslan Dustin Spicuzza Ivor Wanders Jakob Lykke Andersen Michael Šimáček Robert Haschke Tomi Valkeinen Xiaofei Wang Henry Schreiner
parent d8565ac731
commit 2943a27a14
66 changed files with 5533 additions and 169 deletions
+247
View File
@@ -0,0 +1,247 @@
#include "pybind11_tests.h"
#include <memory>
#include <string>
#include <vector>
namespace pybind11_tests {
namespace class_sh_basic {
struct atyp { // Short for "any type".
std::string mtxt;
atyp() : mtxt("DefaultConstructor") {}
explicit atyp(const std::string &mtxt_) : mtxt(mtxt_) {}
atyp(const atyp &other) { mtxt = other.mtxt + "_CpCtor"; }
atyp(atyp &&other) noexcept { mtxt = other.mtxt + "_MvCtor"; }
};
struct uconsumer { // unique_ptr consumer
std::unique_ptr<atyp> held;
bool valid() const { return static_cast<bool>(held); }
void pass_valu(std::unique_ptr<atyp> obj) { held = std::move(obj); }
void pass_rref(std::unique_ptr<atyp> &&obj) { held = std::move(obj); }
std::unique_ptr<atyp> rtrn_valu() { return std::move(held); }
std::unique_ptr<atyp> &rtrn_lref() { return held; }
const std::unique_ptr<atyp> &rtrn_cref() const { return held; }
};
/// Custom deleter that is default constructible.
struct custom_deleter {
std::string trace_txt;
custom_deleter() = default;
explicit custom_deleter(const std::string &trace_txt_) : trace_txt(trace_txt_) {}
custom_deleter(const custom_deleter &other) { trace_txt = other.trace_txt + "_CpCtor"; }
custom_deleter &operator=(const custom_deleter &rhs) {
trace_txt = rhs.trace_txt + "_CpLhs";
return *this;
}
custom_deleter(custom_deleter &&other) noexcept {
trace_txt = other.trace_txt + "_MvCtorTo";
other.trace_txt += "_MvCtorFrom";
}
custom_deleter &operator=(custom_deleter &&rhs) noexcept {
trace_txt = rhs.trace_txt + "_MvLhs";
rhs.trace_txt += "_MvRhs";
return *this;
}
void operator()(atyp *p) const { std::default_delete<atyp>()(p); }
void operator()(const atyp *p) const { std::default_delete<const atyp>()(p); }
};
static_assert(std::is_default_constructible<custom_deleter>::value, "");
/// Custom deleter that is not default constructible.
struct custom_deleter_nd : custom_deleter {
custom_deleter_nd() = delete;
explicit custom_deleter_nd(const std::string &trace_txt_) : custom_deleter(trace_txt_) {}
};
static_assert(!std::is_default_constructible<custom_deleter_nd>::value, "");
// clang-format off
atyp rtrn_valu() { atyp obj{"rtrn_valu"}; return obj; }
atyp&& rtrn_rref() { static atyp obj; obj.mtxt = "rtrn_rref"; return std::move(obj); }
atyp const& rtrn_cref() { static atyp obj; obj.mtxt = "rtrn_cref"; return obj; }
atyp& rtrn_mref() { static atyp obj; obj.mtxt = "rtrn_mref"; return obj; }
atyp const* rtrn_cptr() { return new atyp{"rtrn_cptr"}; }
atyp* rtrn_mptr() { return new atyp{"rtrn_mptr"}; }
std::string pass_valu(atyp obj) { return "pass_valu:" + obj.mtxt; } // NOLINT
std::string pass_cref(atyp const& obj) { return "pass_cref:" + obj.mtxt; }
std::string pass_mref(atyp& obj) { return "pass_mref:" + obj.mtxt; }
std::string pass_cptr(atyp const* obj) { return "pass_cptr:" + obj->mtxt; }
std::string pass_mptr(atyp* obj) { return "pass_mptr:" + obj->mtxt; }
std::shared_ptr<atyp> rtrn_shmp() { return std::make_shared<atyp>("rtrn_shmp"); }
std::shared_ptr<atyp const> rtrn_shcp() { return std::shared_ptr<atyp const>(new atyp{"rtrn_shcp"}); }
std::string pass_shmp(std::shared_ptr<atyp> obj) { return "pass_shmp:" + obj->mtxt; } // NOLINT
std::string pass_shcp(std::shared_ptr<atyp const> obj) { return "pass_shcp:" + obj->mtxt; } // NOLINT
std::unique_ptr<atyp> rtrn_uqmp() { return std::unique_ptr<atyp >(new atyp{"rtrn_uqmp"}); }
std::unique_ptr<atyp const> rtrn_uqcp() { return std::unique_ptr<atyp const>(new atyp{"rtrn_uqcp"}); }
std::string pass_uqmp(std::unique_ptr<atyp > obj) { return "pass_uqmp:" + obj->mtxt; }
std::string pass_uqcp(std::unique_ptr<atyp const> obj) { return "pass_uqcp:" + obj->mtxt; }
struct sddm : std::default_delete<atyp > {};
struct sddc : std::default_delete<atyp const> {};
std::unique_ptr<atyp, sddm> rtrn_udmp() { return std::unique_ptr<atyp, sddm>(new atyp{"rtrn_udmp"}); }
std::unique_ptr<atyp const, sddc> rtrn_udcp() { return std::unique_ptr<atyp const, sddc>(new atyp{"rtrn_udcp"}); }
std::string pass_udmp(std::unique_ptr<atyp, sddm> obj) { return "pass_udmp:" + obj->mtxt; }
std::string pass_udcp(std::unique_ptr<atyp const, sddc> obj) { return "pass_udcp:" + obj->mtxt; }
std::unique_ptr<atyp, custom_deleter> rtrn_udmp_del() { return std::unique_ptr<atyp, custom_deleter>(new atyp{"rtrn_udmp_del"}, custom_deleter{"udmp_deleter"}); }
std::unique_ptr<atyp const, custom_deleter> rtrn_udcp_del() { return std::unique_ptr<atyp const, custom_deleter>(new atyp{"rtrn_udcp_del"}, custom_deleter{"udcp_deleter"}); }
std::string pass_udmp_del(std::unique_ptr<atyp, custom_deleter> obj) { return "pass_udmp_del:" + obj->mtxt + "," + obj.get_deleter().trace_txt; }
std::string pass_udcp_del(std::unique_ptr<atyp const, custom_deleter> obj) { return "pass_udcp_del:" + obj->mtxt + "," + obj.get_deleter().trace_txt; }
std::unique_ptr<atyp, custom_deleter_nd> rtrn_udmp_del_nd() { return std::unique_ptr<atyp, custom_deleter_nd>(new atyp{"rtrn_udmp_del_nd"}, custom_deleter_nd{"udmp_deleter_nd"}); }
std::unique_ptr<atyp const, custom_deleter_nd> rtrn_udcp_del_nd() { return std::unique_ptr<atyp const, custom_deleter_nd>(new atyp{"rtrn_udcp_del_nd"}, custom_deleter_nd{"udcp_deleter_nd"}); }
std::string pass_udmp_del_nd(std::unique_ptr<atyp, custom_deleter_nd> obj) { return "pass_udmp_del_nd:" + obj->mtxt + "," + obj.get_deleter().trace_txt; }
std::string pass_udcp_del_nd(std::unique_ptr<atyp const, custom_deleter_nd> obj) { return "pass_udcp_del_nd:" + obj->mtxt + "," + obj.get_deleter().trace_txt; }
// clang-format on
// Helpers for testing.
std::string get_mtxt(atyp const &obj) { return obj.mtxt; }
std::ptrdiff_t get_ptr(atyp const &obj) { return reinterpret_cast<std::ptrdiff_t>(&obj); }
std::unique_ptr<atyp> unique_ptr_roundtrip(std::unique_ptr<atyp> obj) { return obj; }
std::string pass_unique_ptr_cref(const std::unique_ptr<atyp> &obj) { return obj->mtxt; }
const std::unique_ptr<atyp> &rtrn_unique_ptr_cref(const std::string &mtxt) {
static std::unique_ptr<atyp> obj{new atyp{"static_ctor_arg"}};
if (!mtxt.empty()) {
obj->mtxt = mtxt;
}
return obj;
}
const std::unique_ptr<atyp> &unique_ptr_cref_roundtrip(const std::unique_ptr<atyp> &obj) {
return obj;
}
struct SharedPtrStash {
std::vector<std::shared_ptr<const atyp>> stash;
void Add(const std::shared_ptr<const atyp> &obj) { stash.push_back(obj); }
};
class LocalUnusualOpRef : UnusualOpRef {}; // To avoid clashing with `py::class_<UnusualOpRef>`.
py::object CastUnusualOpRefConstRef(const LocalUnusualOpRef &cref) { return py::cast(cref); }
py::object CastUnusualOpRefMovable(LocalUnusualOpRef &&mvbl) { return py::cast(std::move(mvbl)); }
TEST_SUBMODULE(class_sh_basic, m) {
namespace py = pybind11;
py::classh<atyp>(m, "atyp").def(py::init<>()).def(py::init([](const std::string &mtxt) {
atyp obj;
obj.mtxt = mtxt;
return obj;
}));
m.def("rtrn_valu", rtrn_valu);
m.def("rtrn_rref", rtrn_rref);
m.def("rtrn_cref", rtrn_cref);
m.def("rtrn_mref", rtrn_mref);
m.def("rtrn_cptr", rtrn_cptr);
m.def("rtrn_mptr", rtrn_mptr);
m.def("pass_valu", pass_valu);
m.def("pass_cref", pass_cref);
m.def("pass_mref", pass_mref);
m.def("pass_cptr", pass_cptr);
m.def("pass_mptr", pass_mptr);
m.def("rtrn_shmp", rtrn_shmp);
m.def("rtrn_shcp", rtrn_shcp);
m.def("pass_shmp", pass_shmp);
m.def("pass_shcp", pass_shcp);
m.def("rtrn_uqmp", rtrn_uqmp);
m.def("rtrn_uqcp", rtrn_uqcp);
m.def("pass_uqmp", pass_uqmp);
m.def("pass_uqcp", pass_uqcp);
m.def("rtrn_udmp", rtrn_udmp);
m.def("rtrn_udcp", rtrn_udcp);
m.def("pass_udmp", pass_udmp);
m.def("pass_udcp", pass_udcp);
m.def("rtrn_udmp_del", rtrn_udmp_del);
m.def("rtrn_udcp_del", rtrn_udcp_del);
m.def("pass_udmp_del", pass_udmp_del);
m.def("pass_udcp_del", pass_udcp_del);
m.def("rtrn_udmp_del_nd", rtrn_udmp_del_nd);
m.def("rtrn_udcp_del_nd", rtrn_udcp_del_nd);
m.def("pass_udmp_del_nd", pass_udmp_del_nd);
m.def("pass_udcp_del_nd", pass_udcp_del_nd);
py::classh<uconsumer>(m, "uconsumer")
.def(py::init<>())
.def("valid", &uconsumer::valid)
.def("pass_valu", &uconsumer::pass_valu)
.def("pass_rref", &uconsumer::pass_rref)
.def("rtrn_valu", &uconsumer::rtrn_valu)
.def("rtrn_lref", &uconsumer::rtrn_lref)
.def("rtrn_cref", &uconsumer::rtrn_cref);
// Helpers for testing.
// These require selected functions above to work first, as indicated:
m.def("get_mtxt", get_mtxt); // pass_cref
m.def("get_ptr", get_ptr); // pass_cref
m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_uqmp, rtrn_uqmp
m.def("pass_unique_ptr_cref", pass_unique_ptr_cref);
m.def("rtrn_unique_ptr_cref", rtrn_unique_ptr_cref);
m.def("unique_ptr_cref_roundtrip", unique_ptr_cref_roundtrip);
py::classh<SharedPtrStash>(m, "SharedPtrStash")
.def(py::init<>())
.def("Add", &SharedPtrStash::Add, py::arg("obj"));
m.def("py_type_handle_of_atyp", []() {
return py::type::handle_of<atyp>(); // Exercises static_cast in this function.
});
// Checks for type names used as arguments
m.def("args_shared_ptr", [](std::shared_ptr<atyp> p) { return p; });
m.def("args_shared_ptr_const", [](std::shared_ptr<atyp const> p) { return p; });
m.def("args_unique_ptr", [](std::unique_ptr<atyp> p) { return p; });
m.def("args_unique_ptr_const", [](std::unique_ptr<atyp const> p) { return p; });
// Make sure unique_ptr type caster accept automatic_reference return value policy.
m.def(
"rtrn_uq_automatic_reference",
[]() { return std::unique_ptr<atyp>(new atyp("rtrn_uq_automatic_reference")); },
pybind11::return_value_policy::automatic_reference);
m.def("pass_shared_ptr_ptr", [](std::shared_ptr<atyp> *) {});
py::classh<LocalUnusualOpRef>(m, "LocalUnusualOpRef");
m.def("CallCastUnusualOpRefConstRef",
[]() { return CastUnusualOpRefConstRef(LocalUnusualOpRef()); });
m.def("CallCastUnusualOpRefMovable",
[]() { return CastUnusualOpRefMovable(LocalUnusualOpRef()); });
}
} // namespace class_sh_basic
} // namespace pybind11_tests