Skip to content

[smart_holder] return value policies allowed for shared_ptr #3039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions include/pybind11/detail/smart_holder_type_casters.h
Original file line number Diff line number Diff line change
Expand Up @@ -623,11 +623,16 @@ struct smart_holder_type_caster<std::shared_ptr<T>> : smart_holder_type_caster_l
static constexpr auto name = _<std::shared_ptr<T>>();

static handle cast(const std::shared_ptr<T> &src, return_value_policy policy, handle parent) {
if (policy != return_value_policy::automatic
&& policy != return_value_policy::reference_internal
&& policy != return_value_policy::automatic_reference) {
// SMART_HOLDER_WIP: IMPROVABLE: Error message.
throw cast_error("Invalid return_value_policy for shared_ptr.");
switch (policy) {
case return_value_policy::automatic: break;
case return_value_policy::automatic_reference: break;
case return_value_policy::take_ownership:
throw cast_error("Invalid return_value_policy for shared_ptr (take_ownership).");
case return_value_policy::copy: break;
case return_value_policy::move: break;
case return_value_policy::reference:
throw cast_error("Invalid return_value_policy for shared_ptr (reference).");
case return_value_policy::reference_internal: break;
}
if (!src)
return none().release();
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ set(PYBIND11_TEST_FILES
test_class_sh_disowning_mi.cpp
test_class_sh_factory_constructors.cpp
test_class_sh_inheritance.cpp
test_class_sh_shared_ptr_copy_move.cpp
test_class_sh_trampoline_basic.cpp
test_class_sh_trampoline_self_life_support.cpp
test_class_sh_trampoline_shared_ptr_cpp_arg.cpp
Expand Down
114 changes: 114 additions & 0 deletions tests/test_class_sh_shared_ptr_copy_move.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include "pybind11_tests.h"

#include <pybind11/smart_holder.h>

#include <memory>
#include <string>
#include <vector>

namespace pybind11_tests {
namespace {

const std::string fooNames[] = {"ShPtr_", "SmHld_"};

template <int SerNo>
struct Foo {
std::string history;
Foo(const std::string &history_) : history(history_) {}
Foo(const Foo &other) : history(other.history + "_CpCtor") {}
Foo(Foo &&other) noexcept : history(other.history + "_MvCtor") {}
Foo &operator=(const Foo &other) {
history = other.history + "_OpEqLv";
return *this;
}
Foo &operator=(Foo &&other) noexcept {
history = other.history + "_OpEqRv";
return *this;
}
std::string get_history() const { return "Foo" + fooNames[SerNo] + history; }
};

using FooShPtr = Foo<0>;
using FooSmHld = Foo<1>;

struct Outer {
std::shared_ptr<FooShPtr> ShPtr;
std::shared_ptr<FooSmHld> SmHld;
Outer()
: ShPtr(std::make_shared<FooShPtr>("Outer")), SmHld(std::make_shared<FooSmHld>("Outer")) {}
std::shared_ptr<FooShPtr> getShPtr() const { return ShPtr; }
std::shared_ptr<FooSmHld> getSmHld() const { return SmHld; }
};

} // namespace
} // namespace pybind11_tests

PYBIND11_TYPE_CASTER_BASE_HOLDER(pybind11_tests::FooShPtr,
std::shared_ptr<pybind11_tests::FooShPtr>)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::FooSmHld)

namespace pybind11_tests {
namespace {

TEST_SUBMODULE(class_sh_shared_ptr_copy_move, m) {
namespace py = pybind11;

py::class_<FooShPtr, std::shared_ptr<FooShPtr>>(m, "FooShPtr")
.def("get_history", &FooShPtr::get_history);
py::classh<FooSmHld>(m, "FooSmHld").def("get_history", &FooSmHld::get_history);

auto outer = py::class_<Outer>(m, "Outer").def(py::init());
#define MAKE_PROP(PropTyp) \
MAKE_PROP_FOO(ShPtr, PropTyp) \
MAKE_PROP_FOO(SmHld, PropTyp)

#define MAKE_PROP_FOO(FooTyp, PropTyp) \
.def_##PropTyp(#FooTyp "_" #PropTyp "_default", &Outer::FooTyp) \
.def_##PropTyp( \
#FooTyp "_" #PropTyp "_copy", &Outer::FooTyp, py::return_value_policy::copy) \
.def_##PropTyp( \
#FooTyp "_" #PropTyp "_move", &Outer::FooTyp, py::return_value_policy::move)
outer MAKE_PROP(readonly) MAKE_PROP(readwrite);
#undef MAKE_PROP_FOO

#define MAKE_PROP_FOO(FooTyp, PropTyp) \
.def_##PropTyp(#FooTyp "_property_" #PropTyp "_default", &Outer::FooTyp) \
.def_property_##PropTyp(#FooTyp "_property_" #PropTyp "_copy", \
&Outer::get##FooTyp, \
py::return_value_policy::copy) \
.def_property_##PropTyp(#FooTyp "_property_" #PropTyp "_move", \
&Outer::get##FooTyp, \
py::return_value_policy::move)
outer MAKE_PROP(readonly);
#undef MAKE_PROP_FOO
#undef MAKE_PROP

m.def("test_ShPtr_copy", []() {
auto o = std::make_shared<FooShPtr>("copy");
auto l = py::list();
l.append(o);
return l;
});
m.def("test_SmHld_copy", []() {
auto o = std::make_shared<FooSmHld>("copy");
auto l = py::list();
l.append(o);
return l;
});

m.def("test_ShPtr_move", []() {
auto o = std::make_shared<FooShPtr>("move");
auto l = py::list();
l.append(std::move(o));
return l;
});
m.def("test_SmHld_move", []() {
auto o = std::make_shared<FooSmHld>("move");
auto l = py::list();
l.append(std::move(o));
return l;
});
}

} // namespace
} // namespace pybind11_tests
41 changes: 41 additions & 0 deletions tests/test_class_sh_shared_ptr_copy_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-

from pybind11_tests import class_sh_shared_ptr_copy_move as m


def test_shptr_copy():
txt = m.test_ShPtr_copy()[0].get_history()
assert txt == "FooShPtr_copy"


def test_smhld_copy():
txt = m.test_SmHld_copy()[0].get_history()
assert txt == "FooSmHld_copy"


def test_shptr_move():
txt = m.test_ShPtr_move()[0].get_history()
assert txt == "FooShPtr_move"


def test_smhld_move():
txt = m.test_SmHld_move()[0].get_history()
assert txt == "FooSmHld_move"


def _check_property(foo_typ, prop_typ, policy):
o = m.Outer()
name = "{}_{}_{}".format(foo_typ, prop_typ, policy)
history = "Foo{}_Outer".format(foo_typ)
f = getattr(o, name)
assert f.get_history() == history
# and try again to check that o did not get changed
f = getattr(o, name)
assert f.get_history() == history


def test_properties():
for prop_typ in ("readonly", "readwrite", "property_readonly"):
for foo_typ in ("ShPtr", "SmHld"):
for policy in ("default", "copy", "move"):
_check_property(foo_typ, prop_typ, policy)