diff --git a/include/boost/python/converter/object_manager.hpp b/include/boost/python/converter/object_manager.hpp index 6ffe6b1e..23e70a1e 100755 --- a/include/boost/python/converter/object_manager.hpp +++ b/include/boost/python/converter/object_manager.hpp @@ -21,8 +21,9 @@ class object; namespace boost { namespace python { namespace converter { template -class is_object_manager +struct is_object_manager { + private: BOOST_STATIC_CONSTANT(bool, hdl = is_handle::value); BOOST_STATIC_CONSTANT(bool, borrowed = python::detail::is_borrowed_ptr::value); public: diff --git a/include/boost/python/object.hpp b/include/boost/python/object.hpp index 30b007c4..b2e0c6b3 100755 --- a/include/boost/python/object.hpp +++ b/include/boost/python/object.hpp @@ -9,6 +9,7 @@ # include # include # include +# include # include namespace boost { namespace python { diff --git a/include/boost/python/object_attributes.hpp b/include/boost/python/object_attributes.hpp index 4c8458a4..c10608cb 100755 --- a/include/boost/python/object_attributes.hpp +++ b/include/boost/python/object_attributes.hpp @@ -14,12 +14,14 @@ namespace boost { namespace python { namespace api { struct const_attribute_policies { - static object get(object const& target, object const& key); + typedef char const* key_type; + static object get(object const& target, char const* key); }; struct attribute_policies : const_attribute_policies { - static object const& set(object const& target, object const& key, object const& value); + static object const& set(object const& target, char const* key, object const& value); + static void del(object const&target, char const* key); }; // @@ -28,55 +30,38 @@ struct attribute_policies : const_attribute_policies template inline object_attribute object_operators::attr(char const* name) { - object const& x = *static_cast(this); - return object_attribute(x, object(name)); + object_cref x = *static_cast(this); + return object_attribute(x, name); } template inline const_object_attribute object_operators::attr(char const* name) const { - object const& x = *static_cast(this); - return const_object_attribute(x, object(name)); + object_cref x = *static_cast(this); + return const_object_attribute(x, name); } - -inline object const_attribute_policies::get(object const& target, object const& key) +inline object const_attribute_policies::get(object const& target, char const* key) { return python::getattr(target, key); } inline object const& attribute_policies::set( object const& target - , object const& key + , char const* key , object const& value) { python::setattr(target, key, value); return value; } -} // namespace api - -namespace converter +inline void attribute_policies::del( + object const& target + , char const* key) { - // These specializations are a lie; the proxies do not directly - // manage an object. What actually happens is that the implicit - // conversion to object takes hold and its conversion to_python is - // used. That's OK in part because the object temporary *is* - // actually managing the object during the duration of the - // conversion. - template <> - struct is_object_manager - { - BOOST_STATIC_CONSTANT(bool, value = true); - }; - - template <> - struct is_object_manager - { - BOOST_STATIC_CONSTANT(bool, value = true); - }; + python::delattr(target, key); } -}} // namespace boost::python +}}} // namespace boost::python::api #endif // OBJECT_ATTRIBUTES_DWA2002615_HPP diff --git a/include/boost/python/object_core.hpp b/include/boost/python/object_core.hpp index 762bdf90..b0b9813f 100755 --- a/include/boost/python/object_core.hpp +++ b/include/boost/python/object_core.hpp @@ -10,6 +10,8 @@ # include # include # include +# include +# include namespace boost { namespace python { @@ -27,12 +29,74 @@ namespace api struct attribute_policies; struct const_item_policies; struct item_policies; + struct const_slice_policies; + struct slice_policies; typedef proxy const_object_attribute; typedef proxy object_attribute; typedef proxy const_object_item; typedef proxy object_item; + typedef proxy const_object_slice; + typedef proxy object_slice; + // + // is_proxy -- proxy type detection + // +# ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + template + struct is_proxy + { + BOOST_STATIC_CONSTANT(bool, value = false); + }; + template + struct is_proxy > + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; +# else + typedef char yes_proxy; + typedef char (&no_proxy)[2]; + template + yes_proxy is_proxy_helper(boost::type >*); + no_proxy is_proxy_helper(...); + template + struct is_proxy + { + BOOST_STATIC_CONSTANT( + bool, value = (sizeof(is_proxy_helper((boost::type*)0)) + == sizeof(yes_proxy))); + }; +# endif + + // + // object_handle -- get the handle to construct the object with, + // based on whether T is a proxy or not + // + template + struct object_handle + { + template + static handle<> get(T const& x) + { + return handle<>( + python::borrowed( + python::allow_null( // null check is already done + converter::arg_to_python(x).get()) + ) + ); + } + }; + + template <> + struct object_handle + { + template + static handle<> get(proxy const& x) + { + return x.operator object().ptr(); + } + }; + // A way to turn a conrete type T into a type dependent on U. This // keeps conforming compilers from complaining about returning an // incomplete T from a template member function (which must be @@ -54,12 +118,19 @@ namespace api # else typedef object self_cref; # endif + + // there appears to be a codegen bug here. We prevent the early + // destruction of a temporary in CWPro8 by binding a named + // object instead. +# if __MWERKS__ != 0x3000 + typedef object const& object_cref; +# else + typedef object const object_cref; +# endif public: - // Attribute access via x.attr("attribute_name") - const_object_attribute attr(char const*) const; - object_attribute attr(char const*); - + // function call + // object operator()() const; # ifndef BOOST_PYTHON_GENERATE_CODE @@ -81,10 +152,17 @@ namespace api , BOOST_PYTHON_OBJECT_CALL, ignored) // truth value testing + // operator bool_type() const; bool operator!() const; // needed for vc6 + // Attribute access + // + const_object_attribute attr(char const*) const; + object_attribute attr(char const*); + // item access + // const_object_item operator[](self_cref) const; object_item operator[](self_cref); @@ -110,6 +188,47 @@ namespace api } # endif + // slicing + // + const_object_slice slice(self_cref, self_cref) const; + object_slice slice(self_cref, self_cref); + + const_object_slice slice(slice_nil, self_cref) const; + object_slice slice(slice_nil, self_cref); + + const_object_slice slice(self_cref, slice_nil) const; + object_slice slice(self_cref, slice_nil); + +# if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 + template + const_object_slice + slice(T const& start, V const& end) const; + + template + object_slice + slice(T const& start, V const& end); + +# else + template + const_object_slice + slice(T const& start, V const& end) const + { + return this->slice( + slice_bound::type(start) + , slice_bound::type(end)); + } + + template + object_slice + slice(T const& start, V const& end) + { + return this->slice( + slice_bound::type(start) + , slice_bound::type(end)); + } +# endif + + # if BOOST_MSVC == 1200 // For whatever reason, VC6 generates incorrect code unless we // define this @@ -128,12 +247,7 @@ namespace api // explicit conversion from any C++ object to Python template explicit object(T const& x) - : m_ptr( - python::borrowed( - python::allow_null( // null check is already done - converter::arg_to_python(x).get()) - ) - ) + : m_ptr(object_handle::value>::get(x)) { } @@ -141,12 +255,7 @@ namespace api // literals. Otherwise, they get deduced as char[N]const& above // and conversion fails at runtime. explicit object(char const* x) - : m_ptr( - python::borrowed( - python::allow_null( // null check is already done - converter::arg_to_python(x).get()) - ) - ) + : m_ptr(object_handle<>::get(x)) { } diff --git a/include/boost/python/object_items.hpp b/include/boost/python/object_items.hpp index a91ebcd8..e07a9e50 100755 --- a/include/boost/python/object_items.hpp +++ b/include/boost/python/object_items.hpp @@ -14,12 +14,14 @@ namespace boost { namespace python { namespace api { struct const_item_policies { + typedef object key_type; static object get(object const& target, object const& key); }; struct item_policies : const_item_policies { static object const& set(object const& target, object const& key, object const& value); + static void del(object const& target, object const& key); }; // @@ -29,7 +31,7 @@ template inline object_item object_operators::operator[](self_cref key) { - object const& x = *static_cast(this); + object_cref x = *static_cast(this); return object_item(x, key); } @@ -37,7 +39,7 @@ template inline const_object_item object_operators::operator[](self_cref key) const { - object const& x = *static_cast(this); + object_cref x = *static_cast(this); return const_object_item(x, key); } @@ -62,7 +64,7 @@ object_operators::operator[](T const& key) inline object const_item_policies::get(object const& target, object const& key) { - return python::getitem(target, key); + return getitem(target, key); } inline object const& item_policies::set( @@ -70,33 +72,17 @@ inline object const& item_policies::set( , object const& key , object const& value) { - python::setitem(target, key, value); + setitem(target, key, value); return value; } -} // namespace api - -namespace converter +inline void item_policies::del( + object const& target + , object const& key) { - // These specializations are a lie; the proxies do not directly - // manage an object. What actually happens is that the implicit - // conversion to object takes hold and its conversion to_python is - // used. That's OK in part because the object temporary *is* - // actually managing the object during the duration of the - // conversion. - template <> - struct is_object_manager - { - BOOST_STATIC_CONSTANT(bool, value = true); - }; - - template <> - struct is_object_manager - { - BOOST_STATIC_CONSTANT(bool, value = true); - }; + delitem(target, key); } -}} // namespace boost::python +}}} // namespace boost::python::api #endif // OBJECT_ITEMS_DWA2002615_HPP diff --git a/include/boost/python/object_protocol.hpp b/include/boost/python/object_protocol.hpp index 27461e26..fe21be17 100755 --- a/include/boost/python/object_protocol.hpp +++ b/include/boost/python/object_protocol.hpp @@ -11,7 +11,7 @@ # include # include -namespace boost { namespace python { +namespace boost { namespace python { namespace api { template object getattr(Target const& target, Key const& key) @@ -26,6 +26,12 @@ void setattr(object const& target, Key const& key, Value const& value) return setattr(target, object(key), object(value)); } +template +void delattr(object const& target, Key const& key) +{ + delattr(target, object(key)); +} + template object getitem(Target const& target, Key const& key) { @@ -39,7 +45,30 @@ void setitem(object const& target, Key const& key, Value const& value) return setitem(target, object(key), object(value)); } +template +void delitem(object const& target, Key const& key) +{ + delitem(target, object(key)); +} -}} // namespace boost::python +template +object getslice(Target const& target, Begin const& begin, End const& end) +{ + return getslice(object(target), object(begin), object(end)); +} + +template +void setslice(object const& target, Begin const& begin, End const& end, Value const& value) +{ + return setslice(target, object(begin), object(end), object(value)); +} + +template +void delslice(object const& target, Begin const& begin, End const& end) +{ + delslice(target, object(begin), object(end)); +} + +}}} // namespace boost::python::api #endif // OBJECT_PROTOCOL_DWA2002615_HPP diff --git a/include/boost/python/object_protocol_core.hpp b/include/boost/python/object_protocol_core.hpp index 9fc28415..548f9c7a 100755 --- a/include/boost/python/object_protocol_core.hpp +++ b/include/boost/python/object_protocol_core.hpp @@ -6,6 +6,8 @@ #ifndef OBJECT_PROTOCOL_CORE_DWA2002615_HPP # define OBJECT_PROTOCOL_CORE_DWA2002615_HPP +# include + namespace boost { namespace python { namespace api @@ -14,14 +16,34 @@ namespace api BOOST_PYTHON_DECL object getattr(object const& target, object const& key); BOOST_PYTHON_DECL void setattr(object const& target, object const& key, object const& value); + BOOST_PYTHON_DECL void delattr(object const& target, object const& key); + + // These are defined for efficiency, since attributes are commonly + // accessed through literal strings. + BOOST_PYTHON_DECL object getattr(object const& target, char const* key); + BOOST_PYTHON_DECL void setattr(object const& target, char const* key, object const& value); + BOOST_PYTHON_DECL void delattr(object const& target, char const* key); + BOOST_PYTHON_DECL object getitem(object const& target, object const& key); BOOST_PYTHON_DECL void setitem(object const& target, object const& key, object const& value); + BOOST_PYTHON_DECL void delitem(object const& target, object const& key); + + BOOST_PYTHON_DECL object getslice(object const& target, handle<> const& begin, handle<> const& end); + BOOST_PYTHON_DECL void setslice(object const& target, handle<> const& begin, handle<> const& end, object const& value); + BOOST_PYTHON_DECL void delslice(object const& target, handle<> const& begin, handle<> const& end); } using api::getattr; using api::setattr; +using api::delattr; + using api::getitem; using api::setitem; +using api::delitem; + +using api::getslice; +using api::setslice; +using api::delslice; }} // namespace boost::python diff --git a/include/boost/python/object_slices.hpp b/include/boost/python/object_slices.hpp new file mode 100644 index 00000000..3e61d2da --- /dev/null +++ b/include/boost/python/object_slices.hpp @@ -0,0 +1,124 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef OBJECT_SLICES_DWA2002615_HPP +# define OBJECT_SLICES_DWA2002615_HPP + +# include +# include +# include +# include + +namespace boost { namespace python { namespace api { + +struct const_slice_policies +{ + typedef std::pair, handle<> > key_type; + static object get(object const& target, key_type const& key); +}; + +struct slice_policies : const_slice_policies +{ + static object const& set(object const& target, key_type const& key, object const& value); + static void del(object const& target, key_type const& key); +}; + +// +// implementation +// +template +object_slice +object_operators::slice(self_cref start, self_cref finish) +{ + object_cref x = *static_cast(this); + return object_slice(x, std::make_pair(start.ptr(), finish.ptr())); +} + +template +const_object_slice +object_operators::slice(self_cref start, self_cref finish) const +{ + object_cref x = *static_cast(this); + return const_object_slice(x, std::make_pair(start.ptr(), finish.ptr())); +} + +template +object_slice +object_operators::slice(slice_nil, self_cref finish) +{ + object_cref x = *static_cast(this); + return object_slice(x, std::make_pair(handle<>(), finish.ptr())); +} + +template +const_object_slice +object_operators::slice(slice_nil, self_cref finish) const +{ + object_cref x = *static_cast(this); + return const_object_slice(x, std::make_pair(handle<>(), finish.ptr())); +} + +template +object_slice +object_operators::slice(self_cref start, slice_nil) +{ + object_cref x = *static_cast(this); + return object_slice(x, std::make_pair(start.ptr(), handle<>())); +} + +template +const_object_slice +object_operators::slice(self_cref start, slice_nil) const +{ + object_cref x = *static_cast(this); + return const_object_slice(x, std::make_pair(start.ptr(), handle<>())); +} +# if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 +template +template +inline const_object_slice +object_operators::slice(T const& start, V const& end) const +{ + return this->slice( + typename slice_bound::type(start) + , typename slice_bound::type(end)); +} + +template +template +inline object_slice +object_operators::slice(T const& start, V const& end) +{ + return this->slice( + typename slice_bound::type(start) + , typename slice_bound::type(end)); +} +# endif + + +inline object const_slice_policies::get(object const& target, key_type const& key) +{ + return getslice(target, key.first, key.second); +} + +inline object const& slice_policies::set( + object const& target + , key_type const& key + , object const& value) +{ + setslice(target, key.first, key.second, value); + return value; +} + +inline void slice_policies::del( + object const& target + , key_type const& key) +{ + delslice(target, key.first, key.second); +} + +}}} // namespace boost::python::api + +#endif // OBJECT_SLICES_DWA2002615_HPP diff --git a/include/boost/python/proxy.hpp b/include/boost/python/proxy.hpp index b74d155c..385d4b3d 100755 --- a/include/boost/python/proxy.hpp +++ b/include/boost/python/proxy.hpp @@ -13,13 +13,15 @@ namespace boost { namespace python { namespace api { template class proxy : public object_operators > { + typedef typename Policies::key_type key_type; + # if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 typedef proxy const& copy_ctor_self; # else typedef proxy copy_ctor_self; # endif public: - proxy(object const& target, object const& key); + proxy(object const& target, key_type const& key); operator object() const; // to support a[b] = c[d] @@ -31,18 +33,28 @@ class proxy : public object_operators > Policies::set(m_target, m_key, object(rhs)); return *this; } - + + public: // implementation detail + void del() const; + private: object m_target; - object m_key; + key_type m_key; }; + +template +inline void del(proxy const& x) +{ + x.del(); +} + // // implementation // template -inline proxy::proxy(object const& target, object const& key) +inline proxy::proxy(object const& target, key_type const& key) : m_target(target), m_key(key) {} @@ -78,6 +90,12 @@ BOOST_PYTHON_PROXY_INPLACE(^=) BOOST_PYTHON_PROXY_INPLACE(|=) # undef BOOST_PYTHON_PROXY_INPLACE +template +inline void proxy::del() const +{ + Policies::del(m_target, m_key); +} + }}} // namespace boost::python::api #endif // PROXY_DWA2002615_HPP diff --git a/include/boost/python/slice_nil.hpp b/include/boost/python/slice_nil.hpp new file mode 100644 index 00000000..cb169116 --- /dev/null +++ b/include/boost/python/slice_nil.hpp @@ -0,0 +1,41 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef SLICE_NIL_DWA2002620_HPP +# define SLICE_NIL_DWA2002620_HPP + +namespace boost { namespace python { namespace api { + +class object; + +enum slice_nil +{ +# ifndef _ // Watch out for GNU gettext users, who #define _(x) + _ +# endif +}; + +template +struct slice_bound +{ + typedef object type; +}; + +template <> +struct slice_bound +{ + typedef slice_nil type; +}; + +} + +using api::slice_nil; +# ifndef _ // Watch out for GNU gettext users, who #define _(x) +using api::_; +# endif + +}} // namespace boost::python + +#endif // SLICE_NIL_DWA2002620_HPP diff --git a/src/object_protocol.cpp b/src/object_protocol.cpp index 3e9baa35..abcb7994 100755 --- a/src/object_protocol.cpp +++ b/src/object_protocol.cpp @@ -21,6 +21,40 @@ BOOST_PYTHON_DECL void setattr(object const& target, object const& key, object c throw_error_already_set(); } +BOOST_PYTHON_DECL void delattr(object const& target, object const& key) +{ + if (PyObject_DelAttr(target.ptr().get(), key.ptr().get()) == -1) + throw_error_already_set(); +} + +BOOST_PYTHON_DECL object getattr(object const& target, char const* key) +{ + return object( + (object::new_pyobject_reference*) + PyObject_GetAttrString(target.ptr().get(), const_cast(key)) + ); +} + +BOOST_PYTHON_DECL void setattr(object const& target, char const* key, object const& value) +{ + if (PyObject_SetAttrString( + target.ptr().get(), const_cast(key), value.ptr().get()) == -1 + ) + { + throw_error_already_set(); + } +} + +BOOST_PYTHON_DECL void delattr(object const& target, char const* key) +{ + if (PyObject_DelAttrString( + target.ptr().get(), const_cast(key)) == -1 + ) + { + throw_error_already_set(); + } +} + BOOST_PYTHON_DECL object getitem(object const& target, object const& key) { return object((object::new_pyobject_reference*)PyObject_GetItem(target.ptr().get(), key.ptr().get())); @@ -31,5 +65,104 @@ BOOST_PYTHON_DECL void setitem(object const& target, object const& key, object c if (PyObject_SetItem(target.ptr().get(), key.ptr().get(), value.ptr().get()) == -1) throw_error_already_set(); } + +BOOST_PYTHON_DECL void delitem(object const& target, object const& key) +{ + if (PyObject_DelItem(target.ptr().get(), key.ptr().get()) == -1) + throw_error_already_set(); +} + +namespace // slicing code copied directly out of the Python implementation +{ + #undef ISINT + #define ISINT(x) ((x) == NULL || PyInt_Check(x) || PyLong_Check(x)) + + static PyObject * + apply_slice(PyObject *u, PyObject *v, PyObject *w) /* return u[v:w] */ + { + PyTypeObject *tp = u->ob_type; + PySequenceMethods *sq = tp->tp_as_sequence; + + if (sq && sq->sq_slice && ISINT(v) && ISINT(w)) { + int ilow = 0, ihigh = INT_MAX; + if (!_PyEval_SliceIndex(v, &ilow)) + return NULL; + if (!_PyEval_SliceIndex(w, &ihigh)) + return NULL; + return PySequence_GetSlice(u, ilow, ihigh); + } + else { + PyObject *slice = PySlice_New(v, w, NULL); + if (slice != NULL) { + PyObject *res = PyObject_GetItem(u, slice); + Py_DECREF(slice); + return res; + } + else + return NULL; + } + } + + static int + assign_slice(PyObject *u, PyObject *v, PyObject *w, PyObject *x) + /* u[v:w] = x */ + { + PyTypeObject *tp = u->ob_type; + PySequenceMethods *sq = tp->tp_as_sequence; + + if (sq && sq->sq_slice && ISINT(v) && ISINT(w)) { + int ilow = 0, ihigh = INT_MAX; + if (!_PyEval_SliceIndex(v, &ilow)) + return -1; + if (!_PyEval_SliceIndex(w, &ihigh)) + return -1; + if (x == NULL) + return PySequence_DelSlice(u, ilow, ihigh); + else + return PySequence_SetSlice(u, ilow, ihigh, x); + } + else { + PyObject *slice = PySlice_New(v, w, NULL); + if (slice != NULL) { + int res; + if (x != NULL) + res = PyObject_SetItem(u, slice, x); + else + res = PyObject_DelItem(u, slice); + Py_DECREF(slice); + return res; + } + else + return -1; + } + } +} + +BOOST_PYTHON_DECL object getslice(object const& target, handle<> const& begin, handle<> const& end) +{ + return object( + (object::new_pyobject_reference*) + apply_slice(target.ptr().get(), begin.get(), end.get())); +} + +BOOST_PYTHON_DECL void setslice(object const& target, handle<> const& begin, handle<> const& end, object const& value) +{ + if (assign_slice( + target.ptr().get(), begin.get(), end.get(), value.ptr().get()) == -1 + ) + { + throw_error_already_set(); + } +} + +BOOST_PYTHON_DECL void delslice(object const& target, handle<> const& begin, handle<> const& end) +{ + if (assign_slice( + target.ptr().get(), begin.get(), end.get(), 0) == -1 + ) + { + throw_error_already_set(); + } +} }}} // namespace boost::python::api diff --git a/test/object.cpp b/test/object.cpp index e2940e77..9bcf54a4 100755 --- a/test/object.cpp +++ b/test/object.cpp @@ -113,6 +113,22 @@ bool test_not_item(object y, object key) return !y[key]; } +bool check_string_slice() +{ + object s("hello, world"); + + if (s.slice(_,-3) != "hello, wo") + return false; + + if (s.slice(-3,_) != "rld") + return false; + + if (", " != s.slice(5,7)) + return false; + + return s.slice(2,-1).slice(1,-1) == "lo, wor"; +} + bool check_binary_operators() { int y; @@ -295,6 +311,7 @@ BOOST_PYTHON_MODULE_INIT(object_ext) .def("check_binary_operators", check_binary_operators) .def("check_inplace", check_inplace) + .def("check_string_slice", check_string_slice) ; } diff --git a/test/object.py b/test/object.py index e3cb9dc1..54744809 100644 --- a/test/object.py +++ b/test/object.py @@ -80,10 +80,15 @@ >>> test_not_item(d, 'foo') 1 + Slices + +>>> assert check_string_slice() + Operators >>> assert check_binary_operators() + >>> class X: pass ... >>> assert check_inplace(range(3), X())