// 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 ITERATOR_DWA2002510_HPP # define ITERATOR_DWA2002510_HPP # include # include # include # include # include # include # include # include # include # include # include 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. struct default_iterator_call_policies : default_call_policies { struct result_converter { template struct apply { typedef to_python_value type; }; }; }; // Instantiations of these are wrapped to produce Python iterators. template struct iterator_range { iterator_range(ref sequence, Iterator start, Iterator finish); ref m_sequence; // Keeps the sequence alive while iterating. Iterator m_start; Iterator m_finish; }; namespace detail { // Guts of the iterator's next() function. We can't just wrap an // ordinary function because we don't neccessarily know the result // type of dereferencing the iterator. This also saves us from // throwing C++ exceptions to indicate end-of-sequence. template struct iterator_next { static PyObject* execute(PyObject* args_, PyObject* kw, Policies const& policies) { typedef iterator_range range_; PyObject* py_self = PyTuple_GET_ITEM(args_, 0); from_python c0(py_self); range_* self = c0(py_self); // Done iterating? if (self->m_start == self->m_finish) { objects::set_stop_iteration_error(); return 0; } // note: precall happens before we can check for the result // converter in this case, to ensure it happens before the // iterator is dereferenced. However, the arity is 1 so // there's not much risk that this will amount to anything. if (!policies.precall(args_)) return 0; PyObject* result = iterator_next::convert_result(*self->m_start); ++self->m_start; return policies.postcall(args_, result); } private: // Convert the result of dereferencing the iterator. Dispatched // here because we can't neccessarily get the value_type of the // iterator without PTS. This way, we deduce the value type by // dereferencing. template static PyObject* convert_result(ValueType& x) { typedef typename Policies::result_converter result_converter; typename mpl::apply1::type cr; if (!cr.convertible()) return 0; return cr(x); } }; // Get a Python class which contains the given iterator and // policies, creating it if neccessary. Requires: NextPolicies is // default-constructible. template ref demand_iterator_class(char const* name, Iterator* = 0, NextPolicies const& policies = NextPolicies()) { typedef iterator_range range_; // Check the registry. If one is already registered, return it. ref result( objects::registered_class_object(converter::undecorated_type_id())); if (result.get() == 0) { // Make a callable object which can be used as the iterator's next() function. ref next_function( new objects::function( objects::py_function( bind(&detail::iterator_next::execute, _1, _2, policies)) , 1)); result = class_(name) .def("__iter__", identity_function()) .setattr("next", next_function) .object(); } return result; } // This class template acts as a generator for an ordinary function // which builds a Python iterator. template struct make_iterator_help { // 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. static PyObject* create( Accessor1 const& get_start, Accessor2 const& get_finish , PyObject* args_, PyObject* /*kw*/) { // Make sure the Python class is instantiated. demand_iterator_class("iterator"); to_python_value > cr; // This check is probably redundant, since we ensure the // type is registered above. if (!cr.convertible()) return 0; // Extract x from the first argument PyObject* arg0 = PyTuple_GET_ITEM(args_, 0); from_python c0(arg0); if (!c0.convertible()) return 0; typename from_python::result_type x = c0(arg0); // Build and convert the iterator_range<>. return cr( iterator_range( ref(arg0, ref::increment_count) , get_start(x), get_finish(x))); } }; } // 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 inline ref make_iterator_function( Accessor1 const& get_start, Accessor2 const& get_finish , NextPolicies* , boost::type*) { typedef typename Accessor1::result_type result_type; return ref( new objects::function( objects::py_function( boost::bind( &detail::make_iterator_help< Target,result_type,Accessor1,Accessor2,NextPolicies >::create , get_start, get_finish, _1, _2) ) ,1 )); } // // implementation // template inline iterator_range::iterator_range( ref sequence, Iterator start, Iterator finish) : m_sequence(sequence), m_start(start), m_finish(finish) { } }}} // namespace boost::python::objects #endif // ITERATOR_DWA2002510_HPP