Extend tuple and list accessor interface

This commit is contained in:
Dean Moldovan 2016-09-22 23:39:06 +02:00
parent 242b146a51
commit ea763a57a8
4 changed files with 60 additions and 55 deletions

View File

@ -1338,7 +1338,7 @@ NAMESPACE_BEGIN(detail)
PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) {
auto strings = tuple(args.size()); auto strings = tuple(args.size());
for (size_t i = 0; i < args.size(); ++i) { for (size_t i = 0; i < args.size(); ++i) {
strings[i] = args[i].cast<object>().str(); strings[i] = args[i].str();
} }
auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" ");
auto line = sep.attr("join")(strings); auto line = sep.attr("join")(strings);

View File

@ -29,10 +29,14 @@ namespace accessor_policies {
struct obj_attr; struct obj_attr;
struct str_attr; struct str_attr;
struct generic_item; struct generic_item;
struct list_item;
struct tuple_item;
} }
using obj_attr_accessor = accessor<accessor_policies::obj_attr>; using obj_attr_accessor = accessor<accessor_policies::obj_attr>;
using str_attr_accessor = accessor<accessor_policies::str_attr>; using str_attr_accessor = accessor<accessor_policies::str_attr>;
using item_accessor = accessor<accessor_policies::generic_item>; using item_accessor = accessor<accessor_policies::generic_item>;
using list_accessor = accessor<accessor_policies::list_item>;
using tuple_accessor = accessor<accessor_policies::tuple_item>;
/// Tag and check to identify a class which implements the Python object API /// Tag and check to identify a class which implements the Python object API
class pyobject_tag { }; class pyobject_tag { };
@ -241,58 +245,42 @@ struct generic_item {
if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); }
} }
}; };
struct list_item {
using key_type = size_t;
static object get(handle obj, size_t index) {
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); }
return {result, true};
}
static void set(handle obj, size_t index, handle val) {
// PyList_SetItem steals a reference to 'val'
if (PyList_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
throw error_already_set();
}
}
};
struct tuple_item {
using key_type = size_t;
static object get(handle obj, size_t index) {
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
if (!result) { throw error_already_set(); }
return {result, true};
}
static void set(handle obj, size_t index, handle val) {
// PyTuple_SetItem steals a reference to 'val'
if (PyTuple_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
throw error_already_set();
}
}
};
NAMESPACE_END(accessor_policies) NAMESPACE_END(accessor_policies)
struct list_accessor {
public:
list_accessor(handle list, size_t index) : list(list), index(index) { }
void operator=(list_accessor o) { return operator=(object(o)); }
void operator=(const handle &o) {
// PyList_SetItem steals a reference to 'o'
if (PyList_SetItem(list.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
pybind11_fail("Unable to assign value in Python list!");
}
operator object() const {
PyObject *result = PyList_GetItem(list.ptr(), (ssize_t) index);
if (!result)
pybind11_fail("Unable to retrieve value from Python list!");
return object(result, true);
}
template <typename T> T cast() const { return operator object().cast<T>(); }
private:
handle list;
size_t index;
};
struct tuple_accessor {
public:
tuple_accessor(handle tuple, size_t index) : tuple(tuple), index(index) { }
void operator=(tuple_accessor o) { return operator=(object(o)); }
void operator=(const handle &o) {
// PyTuple_SetItem steals a referenceto 'o'
if (PyTuple_SetItem(tuple.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
pybind11_fail("Unable to assign value in Python tuple!");
}
operator object() const {
PyObject *result = PyTuple_GetItem(tuple.ptr(), (ssize_t) index);
if (!result)
pybind11_fail("Unable to retrieve value from Python tuple!");
return object(result, true);
}
template <typename T> T cast() const { return operator object().cast<T>(); }
private:
handle tuple;
size_t index;
};
struct dict_iterator { struct dict_iterator {
public: public:
dict_iterator(handle dict = handle(), ssize_t pos = -1) : dict(dict), pos(pos) { } dict_iterator(handle dict = handle(), ssize_t pos = -1) : dict(dict), pos(pos) { }
@ -647,7 +635,7 @@ public:
if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
} }
size_t size() const { return (size_t) PyTuple_Size(m_ptr); } size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
detail::tuple_accessor operator[](size_t index) const { return detail::tuple_accessor(*this, index); } detail::tuple_accessor operator[](size_t index) const { return {*this, index}; }
}; };
class dict : public object { class dict : public object {
@ -677,7 +665,7 @@ public:
if (!m_ptr) pybind11_fail("Could not allocate list object!"); if (!m_ptr) pybind11_fail("Could not allocate list object!");
} }
size_t size() const { return (size_t) PyList_Size(m_ptr); } size_t size() const { return (size_t) PyList_Size(m_ptr); }
detail::list_accessor operator[](size_t index) const { return detail::list_accessor(*this, index); } detail::list_accessor operator[](size_t index) const { return {*this, index}; }
void append(handle h) const { PyList_Append(m_ptr, h.ptr()); } void append(handle h) const { PyList_Append(m_ptr, h.ptr()); }
}; };

View File

@ -60,7 +60,7 @@ public:
py::list get_list() { py::list get_list() {
py::list list; py::list list;
list.append(py::str("value")); list.append(py::str("value"));
py::print("Entry at position 0:", py::object(list[0])); py::print("Entry at position 0:", list[0]);
list[0] = py::str("overwritten"); list[0] = py::str("overwritten");
return list; return list;
} }
@ -257,4 +257,19 @@ test_initializer python_types([](py::module &m) {
return d; return d;
}); });
m.def("test_tuple_accessor", [](py::tuple existing_t) {
try {
existing_t[0] = py::cast(1);
} catch (const py::error_already_set &) {
// --> Python system error
// Only new tuples (refcount == 1) are mutable
auto new_t = py::tuple(3);
for (size_t i = 0; i < new_t.size(); ++i) {
new_t[i] = py::cast(i);
}
return new_t;
}
return py::tuple();
});
}); });

View File

@ -251,7 +251,7 @@ def test_dict_api():
def test_accessors(): def test_accessors():
from pybind11_tests import test_accessor_api from pybind11_tests import test_accessor_api, test_tuple_accessor
class SubTestObject: class SubTestObject:
attr_obj = 1 attr_obj = 1
@ -278,3 +278,5 @@ def test_accessors():
assert d["is_none"] is False assert d["is_none"] is False
assert d["operator()"] == 2 assert d["operator()"] == 2
assert d["operator*"] == 7 assert d["operator*"] == 7
assert test_tuple_accessor(tuple()) == (0, 1, 2)