From f6b99ede25ab55e4b4170a989e0e3eb899c0de30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20K=C3=B6the?= Date: Thu, 30 Nov 2000 16:13:12 +0000 Subject: [PATCH] added the cursor feature [SVN r8368] --- include/boost/python/class_builder.hpp | 22 ++++- .../boost/python/detail/extension_class.hpp | 92 +++++++++++++++++++ include/boost/python/module_builder.hpp | 20 ++++ 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/include/boost/python/class_builder.hpp b/include/boost/python/class_builder.hpp index 7ef843c4..77c3951a 100644 --- a/include/boost/python/class_builder.hpp +++ b/include/boost/python/class_builder.hpp @@ -144,7 +144,27 @@ class class_builder reference > m_class; }; -// The bug mentioned at the top of this file is that on certain compilers static +namespace detail +{ + +// helper function that does the actual work of creating a cursor for a n +// STL conforming container. called by module_builder::def_cursor_for() +template +void wrap_cursor_class(module_builder & module, + class_builder & wrapped_container) +{ + std::string cursor_name(wrapped_container.get_extension_class()->tp_name); + cursor_name += "_cursor"; + class_builder > cursor_class(module, cursor_name.c_str()); + + cursor_class.def(&cursor::get_item, "__getitem__"); + cursor_class.def(&cursor::set_item, "__setitem__"); + cursor_class.def(&cursor::len, "__len__"); + wrapped_container.def(&extension_class_cursor_factory::get, "cursor"); +} + +} // namespace detail + // global functions declared within the body of a class template will only be // generated when the class template is constructed, and when (for some reason) // the construction does not occur via a new-expression. Otherwise, we could diff --git a/include/boost/python/detail/extension_class.hpp b/include/boost/python/detail/extension_class.hpp index 0e2e3427..78b49413 100644 --- a/include/boost/python/detail/extension_class.hpp +++ b/include/boost/python/detail/extension_class.hpp @@ -319,6 +319,98 @@ class read_only_setattr_function : public function string m_name; }; + +/* helper class to wrap STL conforming iterators. + +Given a wrapped container ("FooList", say), this template is used to create +an auxiliary class "FooList_cursor" that wraps the container's iterator. +The cursor can be used in Python loops likes this: + + >>> for i in foo_list.cursor(): + ... print i.get_data() + +The auxiliary cursor class can be created for any STL conforming +container. It implements random access functions (get_item() and +set_item()) for any iterator, but these will only be as efficient as the +underlying iterator allows. However, this is not a problem because +the above Python loop accesses the items in forward order anyway. +*/ +template +struct cursor +{ + typedef typename Container::iterator iterator; + typedef typename Container::value_type value_type; + + cursor(Container & c, ref python_object) + : m_python_object(python_object), + m_begin(c.begin()), + m_iter(c.begin()), + m_size(c.size()), + m_index(0) + {} + + void advance(int index, std::forward_iterator_tag) + { + if(index < 0 || index >= m_size) + { + PyErr_SetObject(PyExc_KeyError, BOOST_PYTHON_CONVERSION::to_python(index)); + throw python::error_already_set(); + } + + int delta = index - m_index; + if(delta < 0) + { + m_iter = m_begin; + delta = index; + } + std::advance(m_iter, delta); + m_index = index; + } + + void advance(int index, std::bidirectional_iterator_tag) + { + if(index < 0 || index >= m_size) + { + PyErr_SetObject(PyExc_KeyError, BOOST_PYTHON_CONVERSION::to_python(index)); + throw python::error_already_set(); + } + int delta = index - m_index; + std::advance(m_iter, delta); + m_index = index; + } + + value_type const & get_item(int index) + { + advance(index, std::iterator_category(m_iter)); + return *m_iter; + } + + void set_item(int index, value_type const & v) + { + advance(index, std::iterator_category(m_iter)); + *m_iter = v; + } + + int len() const + { return m_size; } + + ref m_python_object; + iterator m_begin, m_iter; + int m_index, m_size; +}; + +/* create a cursor for an STL conforming container */ +template +struct extension_class_cursor_factory +{ + static cursor get(ref container) + { + return cursor( + BOOST_PYTHON_CONVERSION::from_python(container.get(), type()), + container); + } +}; + template struct define_conversion { diff --git a/include/boost/python/module_builder.hpp b/include/boost/python/module_builder.hpp index 6a8a9b4b..e17a8fd5 100644 --- a/include/boost/python/module_builder.hpp +++ b/include/boost/python/module_builder.hpp @@ -16,6 +16,17 @@ namespace boost { namespace python { +class module_builder; + +template class class_builder; + +namespace detail { + +template +void wrap_cursor_class(module_builder & module, + class_builder & wrapped_container); +} // namspace detail + class module_builder { public: @@ -39,6 +50,15 @@ class module_builder { add(detail::new_wrapped_function(fn), name); } + + // wrapped_container must wrap an STL conforming container; + // this function creates a cursor that wraps this container's iterator + // and adds the factory function "cursor()" to the wrapped container + template + void def_cursor_for(class_builder & wrapped_container) + { + detail::wrap_cursor_class(*this, wrapped_container); + } // Return true iff a module is currently being built. static bool initializing();