| // Copyright David Abrahams 2002. |
| // Distributed under the Boost Software License, Version 1.0. (See |
| // accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| #ifndef ITERATOR_DWA2002510_HPP |
| # define ITERATOR_DWA2002510_HPP |
| |
| # include <boost/python/detail/prefix.hpp> |
| |
| # include <boost/python/class.hpp> |
| # include <boost/python/return_value_policy.hpp> |
| # include <boost/python/return_by_value.hpp> |
| # include <boost/python/handle.hpp> |
| # include <boost/python/make_function.hpp> |
| |
| # include <boost/python/object/iterator_core.hpp> |
| # include <boost/python/object/class_detail.hpp> |
| # include <boost/python/object/function_object.hpp> |
| |
| # include <boost/mpl/vector/vector10.hpp> |
| # include <boost/mpl/if.hpp> |
| |
| # include <boost/python/detail/raw_pyobject.hpp> |
| |
| # include <boost/type.hpp> |
| |
| # include <boost/type_traits/is_same.hpp> |
| # include <boost/type_traits/add_reference.hpp> |
| # include <boost/type_traits/add_const.hpp> |
| |
| # include <boost/detail/iterator.hpp> |
| |
| namespace boost { namespace python { namespace objects { |
| |
| // CallPolicies for the next() method of iterators. We don't want |
| // users to have to explicitly specify that the references returned by |
| // iterators are copied, so we just replace the result_converter from |
| // the default_iterator_call_policies with a permissive one which |
| // always copies the result. |
| typedef return_value_policy<return_by_value> default_iterator_call_policies; |
| |
| // Instantiations of these are wrapped to produce Python iterators. |
| template <class NextPolicies, class Iterator> |
| struct iterator_range |
| { |
| iterator_range(object sequence, Iterator start, Iterator finish); |
| |
| typedef boost::detail::iterator_traits<Iterator> traits_t; |
| |
| struct next |
| { |
| typedef typename mpl::if_< |
| is_reference< |
| typename traits_t::reference |
| > |
| , typename traits_t::reference |
| , typename traits_t::value_type |
| >::type result_type; |
| |
| result_type |
| operator()(iterator_range<NextPolicies,Iterator>& self) |
| { |
| if (self.m_start == self.m_finish) |
| stop_iteration_error(); |
| return *self.m_start++; |
| } |
| |
| # if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) |
| // CWPro8 has a codegen problem when this is an empty class |
| int garbage; |
| # endif |
| }; |
| |
| # ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION |
| // for compilers which can't deduce the value_type of pointers, we |
| // have a special implementation of next. This takes advantage of |
| // the fact that T* results are treated like T& results by |
| // Boost.Python's function wrappers. |
| struct next_ptr |
| { |
| typedef Iterator result_type; |
| |
| result_type |
| operator()(iterator_range<NextPolicies,Iterator>& self) |
| { |
| if (self.m_start == self.m_finish) |
| stop_iteration_error(); |
| return self.m_start++; |
| } |
| }; |
| |
| typedef mpl::if_< |
| is_same< |
| boost::detail::please_invoke_BOOST_TT_BROKEN_COMPILER_SPEC_on_cv_unqualified_pointee<Iterator> |
| , typename traits_t::value_type |
| > |
| , next_ptr |
| , next |
| >::type next_fn; |
| # else |
| typedef next next_fn; |
| # endif |
| |
| object m_sequence; // Keeps the sequence alive while iterating. |
| Iterator m_start; |
| Iterator m_finish; |
| }; |
| |
| namespace detail |
| { |
| // Get a Python class which contains the given iterator and |
| // policies, creating it if necessary. Requires: NextPolicies is |
| // default-constructible. |
| template <class Iterator, class NextPolicies> |
| object demand_iterator_class(char const* name, Iterator* = 0, NextPolicies const& policies = NextPolicies()) |
| { |
| typedef iterator_range<NextPolicies,Iterator> range_; |
| |
| // Check the registry. If one is already registered, return it. |
| handle<> class_obj( |
| objects::registered_class_object(python::type_id<range_>())); |
| |
| if (class_obj.get() != 0) |
| return object(class_obj); |
| |
| typedef typename range_::next_fn next_fn; |
| typedef typename next_fn::result_type result_type; |
| |
| return class_<range_>(name, no_init) |
| .def("__iter__", identity_function()) |
| .def( |
| #if PY_VERSION_HEX >= 0x03000000 |
| "__next__" |
| #else |
| "next" |
| #endif |
| , make_function( |
| next_fn() |
| , policies |
| , mpl::vector2<result_type,range_&>() |
| )); |
| } |
| |
| // A function object which builds an iterator_range. |
| template < |
| class Target |
| , class Iterator |
| , class Accessor1 |
| , class Accessor2 |
| , class NextPolicies |
| > |
| struct py_iter_ |
| { |
| py_iter_(Accessor1 const& get_start, Accessor2 const& get_finish) |
| : m_get_start(get_start) |
| , m_get_finish(get_finish) |
| {} |
| |
| // Extract an object x of the Target type from the first Python |
| // argument, and invoke get_start(x)/get_finish(x) to produce |
| // iterators, which are used to construct a new iterator_range<> |
| // object that gets wrapped into a Python iterator. |
| iterator_range<NextPolicies,Iterator> |
| operator()(back_reference<Target&> x) const |
| { |
| // Make sure the Python class is instantiated. |
| detail::demand_iterator_class("iterator", (Iterator*)0, NextPolicies()); |
| |
| return iterator_range<NextPolicies,Iterator>( |
| x.source() |
| , m_get_start(x.get()) |
| , m_get_finish(x.get()) |
| ); |
| } |
| private: |
| Accessor1 m_get_start; |
| Accessor2 m_get_finish; |
| }; |
| |
| template <class Target, class Iterator, class NextPolicies, class Accessor1, class Accessor2> |
| inline object make_iterator_function( |
| Accessor1 const& get_start |
| , Accessor2 const& get_finish |
| , NextPolicies const& /*next_policies*/ |
| , Iterator const& (*)() |
| , boost::type<Target>* |
| , int |
| ) |
| { |
| return make_function( |
| py_iter_<Target,Iterator,Accessor1,Accessor2,NextPolicies>(get_start, get_finish) |
| , default_call_policies() |
| , mpl::vector2<iterator_range<NextPolicies,Iterator>, back_reference<Target&> >() |
| ); |
| } |
| |
| template <class Target, class Iterator, class NextPolicies, class Accessor1, class Accessor2> |
| inline object make_iterator_function( |
| Accessor1 const& get_start |
| , Accessor2 const& get_finish |
| , NextPolicies const& next_policies |
| , Iterator& (*)() |
| , boost::type<Target>* |
| , ...) |
| { |
| return make_iterator_function( |
| get_start |
| , get_finish |
| , next_policies |
| , (Iterator const&(*)())0 |
| , (boost::type<Target>*)0 |
| , 0 |
| ); |
| } |
| |
| } |
| |
| // Create a Python callable object which accepts a single argument |
| // convertible to the C++ Target type and returns a Python |
| // iterator. The Python iterator uses get_start(x) and get_finish(x) |
| // (where x is an instance of Target) to produce begin and end |
| // iterators for the range, and an instance of NextPolicies is used as |
| // CallPolicies for the Python iterator's next() function. |
| template <class Target, class NextPolicies, class Accessor1, class Accessor2> |
| inline object make_iterator_function( |
| Accessor1 const& get_start |
| , Accessor2 const& get_finish |
| , NextPolicies const& next_policies |
| , boost::type<Target>* = 0 |
| ) |
| { |
| typedef typename Accessor1::result_type iterator; |
| typedef typename add_const<iterator>::type iterator_const; |
| typedef typename add_reference<iterator_const>::type iterator_cref; |
| |
| return detail::make_iterator_function( |
| get_start |
| , get_finish |
| , next_policies |
| , (iterator_cref(*)())0 |
| , (boost::type<Target>*)0 |
| , 0 |
| ); |
| } |
| |
| // |
| // implementation |
| // |
| template <class NextPolicies, class Iterator> |
| inline iterator_range<NextPolicies,Iterator>::iterator_range( |
| object sequence, Iterator start, Iterator finish) |
| : m_sequence(sequence), m_start(start), m_finish(finish) |
| { |
| } |
| |
| }}} // namespace boost::python::objects |
| |
| #endif // ITERATOR_DWA2002510_HPP |