Skip to content

Avoid explicitly resetting a std::[experimental::]optional #874

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 1 commit into from
May 27, 2017
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
3 changes: 1 addition & 2 deletions include/pybind11/stl.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,7 @@ template<typename T> struct optional_caster {
if (!src) {
return false;
} else if (src.is_none()) {
value = {}; // nullopt
return true;
return true; // default-constructed value is already empty
}
value_conv inner_caster;
if (!inner_caster.load(src, convert))
Expand Down
24 changes: 24 additions & 0 deletions tests/test_python_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ struct MoveOutContainer {

struct UnregisteredType { };

// Class that can be move- and copy-constructed, but not assigned
struct NoAssign {
int value;

explicit NoAssign(int value = 0) : value(value) {}
NoAssign(const NoAssign &) = default;
NoAssign(NoAssign &&) = default;

NoAssign &operator=(const NoAssign &) = delete;
NoAssign &operator=(NoAssign &&) = delete;
};

test_initializer python_types([](py::module &m) {
/* No constructor is explicitly defined below. An exception is raised when
trying to construct it directly from Python */
Expand Down Expand Up @@ -236,6 +248,10 @@ test_initializer python_types([](py::module &m) {
py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this"));
});

py::class_<NoAssign>(m, "NoAssign", "Class with no C++ assignment operators")
.def(py::init<>())
.def(py::init<int>());

m.def("test_print_failure", []() { py::print(42, UnregisteredType()); });
#if !defined(NDEBUG)
m.attr("debug_enabled") = true;
Expand Down Expand Up @@ -326,6 +342,7 @@ test_initializer python_types([](py::module &m) {
#ifdef PYBIND11_HAS_OPTIONAL
has_optional = true;
using opt_int = std::optional<int>;
using opt_no_assign = std::optional<NoAssign>;
m.def("double_or_zero", [](const opt_int& x) -> int {
return x.value_or(0) * 2;
});
Expand All @@ -335,11 +352,15 @@ test_initializer python_types([](py::module &m) {
m.def("test_nullopt", [](opt_int x) {
return x.value_or(42);
}, py::arg_v("x", std::nullopt, "None"));
m.def("test_no_assign", [](const opt_no_assign &x) {
return x ? x->value : 42;
}, py::arg_v("x", std::nullopt, "None"));
#endif

#ifdef PYBIND11_HAS_EXP_OPTIONAL
has_exp_optional = true;
using exp_opt_int = std::experimental::optional<int>;
using exp_opt_no_assign = std::experimental::optional<NoAssign>;
m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int {
return x.value_or(0) * 2;
});
Expand All @@ -349,6 +370,9 @@ test_initializer python_types([](py::module &m) {
m.def("test_nullopt_exp", [](exp_opt_int x) {
return x.value_or(42);
}, py::arg_v("x", std::experimental::nullopt, "None"));
m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) {
return x ? x->value : 42;
}, py::arg_v("x", std::experimental::nullopt, "None"));
#endif

m.attr("has_optional") = has_optional;
Expand Down
16 changes: 14 additions & 2 deletions tests/test_python_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ def func(self, x, *args):

@pytest.mark.skipif(not has_optional, reason='no <optional>')
def test_optional():
from pybind11_tests import double_or_zero, half_or_none, test_nullopt
from pybind11_tests import (double_or_zero, half_or_none, test_nullopt,
test_no_assign, NoAssign)

assert double_or_zero(None) == 0
assert double_or_zero(42) == 84
Expand All @@ -352,10 +353,16 @@ def test_optional():
assert test_nullopt(42) == 42
assert test_nullopt(43) == 43

assert test_no_assign() == 42
assert test_no_assign(None) == 42
assert test_no_assign(NoAssign(43)) == 43
pytest.raises(TypeError, test_no_assign, 43)


@pytest.mark.skipif(not has_exp_optional, reason='no <experimental/optional>')
def test_exp_optional():
from pybind11_tests import double_or_zero_exp, half_or_none_exp, test_nullopt_exp
from pybind11_tests import (double_or_zero_exp, half_or_none_exp, test_nullopt_exp,
test_no_assign_exp, NoAssign)

assert double_or_zero_exp(None) == 0
assert double_or_zero_exp(42) == 84
Expand All @@ -370,6 +377,11 @@ def test_exp_optional():
assert test_nullopt_exp(42) == 42
assert test_nullopt_exp(43) == 43

assert test_no_assign_exp() == 42
assert test_no_assign_exp(None) == 42
assert test_no_assign_exp(NoAssign(43)) == 43
pytest.raises(TypeError, test_no_assign_exp, 43)


@pytest.mark.skipif(not hasattr(pybind11_tests, "load_variant"), reason='no <variant>')
def test_variant(doc):
Expand Down