Skip to content

add tests for boost::optional casters #3332

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

Closed
wants to merge 22 commits into from
Closed
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
4 changes: 2 additions & 2 deletions include/pybind11/stl.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ template<typename T> struct optional_caster {
static handle cast(T_ &&src, return_value_policy policy, handle parent) {
if (!src)
return none().inc_ref();
if (!std::is_lvalue_reference<T>::value) {
policy = return_value_policy_override<T>::policy(policy);
if (!std::is_lvalue_reference<T_>::value) {
policy = return_value_policy_override<typename T::value_type>::policy(policy);
}
return value_conv::cast(*std::forward<T_>(src), policy, parent);
}
Expand Down
49 changes: 41 additions & 8 deletions tests/test_stl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@
#include <string>

// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14
#if defined(PYBIND11_HAS_VARIANT)
#if defined(PYBIND11_HAS_OPTIONAL) && defined(PYBIND11_HAS_VARIANT)
using std::nullopt;
using std::nullopt_t;
using std::optional;
using std::variant;
#elif defined(PYBIND11_TEST_BOOST) && (!defined(_MSC_VER) || _MSC_VER >= 1910)
# include <boost/none.hpp>
# include <boost/optional.hpp>
# define PYBIND11_HAS_OPTIONAL 1
template <typename T> using optional = boost::optional<T>;
using nullopt_t = boost::none_t;
const nullopt_t nullopt = boost::none;
# include <boost/variant.hpp>
# define PYBIND11_HAS_VARIANT 1
using boost::variant;
Expand All @@ -38,6 +47,11 @@ struct visit_helper<boost::variant> {
return boost::apply_visitor(args...);
}
};

template <typename T>
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};

template<> struct type_caster<nullopt_t> : public void_caster<nullopt_t> {};
}} // namespace pybind11::detail
#endif

Expand Down Expand Up @@ -192,27 +206,46 @@ TEST_SUBMODULE(stl, m) {
// test_optional
m.attr("has_optional") = true;

using opt_int = std::optional<int>;
using opt_no_assign = std::optional<NoAssign>;
using opt_int = optional<int>;
using opt_no_assign = optional<NoAssign>;
m.def("double_or_zero", [](const opt_int& x) -> int {
return x.value_or(0) * 2;
});
m.def("half_or_none", [](int x) -> opt_int { return x != 0 ? opt_int(x / 2) : opt_int(); });
m.def("test_nullopt", [](opt_int x) {
return x.value_or(42);
}, py::arg_v("x", std::nullopt, "None"));
}, py::arg_v("x", nullopt, "None"));
m.def("test_no_assign", [](const opt_no_assign &x) {
return x ? x->value : 42;
}, py::arg_v("x", std::nullopt, "None"));
}, py::arg_v("x", nullopt, "None"));

m.def("nodefer_none_optional", [](std::optional<int>) { return true; });
m.def("nodefer_none_optional", [](optional<int>) { return true; });
m.def("nodefer_none_optional", [](const py::none &) { return false; });

using opt_holder = OptionalHolder<std::optional, MoveOutDetector>;
using opt_holder = OptionalHolder<optional, MoveOutDetector>;
py::class_<opt_holder>(m, "OptionalHolder", "Class with optional member")
.def(py::init<>())
.def_readonly("member", &opt_holder::member)
.def("member_initialized", &opt_holder::member_initialized);

// issue_3330
enum class IssueKEnum {
k0 = 0,
k1 = 1,
};

struct BoostOptionalIssue {
optional<IssueKEnum> value = IssueKEnum::k1;
};

py::enum_<IssueKEnum>(m, "IssueKEnum").value("k0", IssueKEnum::k0).value("k1", IssueKEnum::k1);

py::class_<BoostOptionalIssue>(m, "BoostOptionalIssue")
.def(py::init<>())
.def_property_readonly(
"by_ref", [](BoostOptionalIssue &a) -> optional<IssueKEnum> & { return a.value; })
.def_property_readonly(
"by_copy", [](BoostOptionalIssue &a) -> optional<IssueKEnum> { return a.value; });
#endif

#ifdef PYBIND11_HAS_EXP_OPTIONAL
Expand Down Expand Up @@ -279,7 +312,7 @@ TEST_SUBMODULE(stl, m) {
m.def("tpl_ctor_map", [](std::unordered_map<TplCtorClass, TplCtorClass> &) {});
m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {});
#if defined(PYBIND11_HAS_OPTIONAL)
m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {});
m.def("tpl_constr_optional", [](optional<TplCtorClass> &) {});
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
m.def("tpl_constr_optional", [](std::experimental::optional<TplCtorClass> &) {});
#endif
Expand Down
4 changes: 4 additions & 0 deletions tests/test_stl.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ def test_optional():
mvalue = holder.member
assert mvalue.initialized
assert holder.member_initialized()
a = m.BoostOptionalIssue().by_ref
assert a is not None
b = m.BoostOptionalIssue().by_copy
assert b is not None


@pytest.mark.skipif(
Expand Down