mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-29 08:32:02 +00:00
Later assignments to accessors should not overwrite the original field
`auto var = l[0]` has a strange quirk: `var` is actually an accessor and not an object, so any later assignment of `var = ...` would modify l[0] instead of `var`. This is surprising compared to the non-auto assignment `py::object var = l[0]; var = ...`. By overloading `operator=` on lvalue/rvalue, the expected behavior is restored even for `auto` variables.
This commit is contained in:
parent
ea763a57a8
commit
2bab5793f7
@ -18,6 +18,7 @@
|
||||
# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
|
||||
# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
|
||||
# pragma warning(disable: 4702) // warning C4702: unreachable code
|
||||
# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified
|
||||
#elif defined(__INTEL_COMPILER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero
|
||||
|
@ -199,16 +199,19 @@ class accessor : public object_api<accessor<Policy>> {
|
||||
public:
|
||||
accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { }
|
||||
|
||||
void operator=(const accessor &a) { operator=(handle(a)); }
|
||||
void operator=(const object &o) { operator=(handle(o)); }
|
||||
void operator=(handle value) { Policy::set(obj, key, value); }
|
||||
void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); }
|
||||
void operator=(const accessor &a) & { operator=(handle(a)); }
|
||||
void operator=(const object &o) && { std::move(*this).operator=(handle(o)); }
|
||||
void operator=(const object &o) & { operator=(handle(o)); }
|
||||
void operator=(handle value) && { Policy::set(obj, key, value); }
|
||||
void operator=(handle value) & { get_cache() = object(value, true); }
|
||||
|
||||
operator object() const { return get_cache(); }
|
||||
PyObject *ptr() const { return get_cache().ptr(); }
|
||||
template <typename T> T cast() const { return get_cache().template cast<T>(); }
|
||||
|
||||
private:
|
||||
const object &get_cache() const {
|
||||
object &get_cache() const {
|
||||
if (!cache) { cache = Policy::get(obj, key); }
|
||||
return cache;
|
||||
}
|
||||
|
@ -272,4 +272,21 @@ test_initializer python_types([](py::module &m) {
|
||||
}
|
||||
return py::tuple();
|
||||
});
|
||||
|
||||
m.def("test_accessor_assignment", []() {
|
||||
auto l = py::list(1);
|
||||
l[0] = py::cast(0);
|
||||
|
||||
auto d = py::dict();
|
||||
d["get"] = l[0];
|
||||
auto var = l[0];
|
||||
d["deferred_get"] = var;
|
||||
l[0] = py::cast(1);
|
||||
d["set"] = l[0];
|
||||
var = py::cast(99); // this assignment should not overwrite l[0]
|
||||
d["deferred_set"] = l[0];
|
||||
d["var"] = var;
|
||||
|
||||
return d;
|
||||
});
|
||||
});
|
||||
|
@ -251,7 +251,7 @@ def test_dict_api():
|
||||
|
||||
|
||||
def test_accessors():
|
||||
from pybind11_tests import test_accessor_api, test_tuple_accessor
|
||||
from pybind11_tests import test_accessor_api, test_tuple_accessor, test_accessor_assignment
|
||||
|
||||
class SubTestObject:
|
||||
attr_obj = 1
|
||||
@ -280,3 +280,10 @@ def test_accessors():
|
||||
assert d["operator*"] == 7
|
||||
|
||||
assert test_tuple_accessor(tuple()) == (0, 1, 2)
|
||||
|
||||
d = test_accessor_assignment()
|
||||
assert d["get"] == 0
|
||||
assert d["deferred_get"] == 0
|
||||
assert d["set"] == 1
|
||||
assert d["deferred_set"] == 1
|
||||
assert d["var"] == 99
|
||||
|
Loading…
Reference in New Issue
Block a user