diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 2c017b4fef..5c0592f80d 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -252,8 +252,8 @@ template 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::value) { - policy = return_value_policy_override::policy(policy); + if (!std::is_lvalue_reference::value) { + policy = return_value_policy_override::policy(policy); } return value_conv::cast(*std::forward(src), policy, parent); } diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 7e3363c5ea..186d758b84 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -20,9 +20,18 @@ #include // 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 +# include +# define PYBIND11_HAS_OPTIONAL 1 +template using optional = boost::optional; +using nullopt_t = boost::none_t; +const nullopt_t nullopt = boost::none; # include # define PYBIND11_HAS_VARIANT 1 using boost::variant; @@ -38,6 +47,11 @@ struct visit_helper { return boost::apply_visitor(args...); } }; + +template +struct type_caster> : optional_caster> {}; + +template<> struct type_caster : public void_caster {}; }} // namespace pybind11::detail #endif @@ -192,27 +206,46 @@ TEST_SUBMODULE(stl, m) { // test_optional m.attr("has_optional") = true; - using opt_int = std::optional; - using opt_no_assign = std::optional; + using opt_int = optional; + using opt_no_assign = optional; 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) { return true; }); + m.def("nodefer_none_optional", [](optional) { return true; }); m.def("nodefer_none_optional", [](const py::none &) { return false; }); - using opt_holder = OptionalHolder; + using opt_holder = OptionalHolder; py::class_(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 value = IssueKEnum::k1; + }; + + py::enum_(m, "IssueKEnum").value("k0", IssueKEnum::k0).value("k1", IssueKEnum::k1); + + py::class_(m, "BoostOptionalIssue") + .def(py::init<>()) + .def_property_readonly( + "by_ref", [](BoostOptionalIssue &a) -> optional & { return a.value; }) + .def_property_readonly( + "by_copy", [](BoostOptionalIssue &a) -> optional { return a.value; }); #endif #ifdef PYBIND11_HAS_EXP_OPTIONAL @@ -279,7 +312,7 @@ TEST_SUBMODULE(stl, m) { m.def("tpl_ctor_map", [](std::unordered_map &) {}); m.def("tpl_ctor_set", [](std::unordered_set &) {}); #if defined(PYBIND11_HAS_OPTIONAL) - m.def("tpl_constr_optional", [](std::optional &) {}); + m.def("tpl_constr_optional", [](optional &) {}); #elif defined(PYBIND11_HAS_EXP_OPTIONAL) m.def("tpl_constr_optional", [](std::experimental::optional &) {}); #endif diff --git a/tests/test_stl.py b/tests/test_stl.py index 3f63d6c3a4..059742d77f 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -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(