Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/boost/python/slice.hpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 #ifndef BOOST_PYTHON_SLICE_JDB20040105_HPP
0002 #define BOOST_PYTHON_SLICE_JDB20040105_HPP
0003 
0004 // Copyright (c) 2004 Jonathan Brandmeyer
0005 //  Use, modification and distribution are subject to the
0006 //  Boost Software License, Version 1.0. (See accompanying file 
0007 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0008 
0009 #include <boost/python/detail/prefix.hpp>
0010 #include <boost/config.hpp>
0011 #include <boost/python/object.hpp>
0012 #include <boost/python/extract.hpp>
0013 #include <boost/python/converter/pytype_object_mgr_traits.hpp>
0014 
0015 #include <boost/iterator/iterator_traits.hpp>
0016 
0017 #include <iterator>
0018 #include <algorithm>
0019 
0020 namespace boost { namespace python {
0021 
0022 namespace detail
0023 {
0024   class BOOST_PYTHON_DECL slice_base : public object
0025   {
0026    public:
0027       // Get the Python objects associated with the slice.  In principle, these 
0028       // may be any arbitrary Python type, but in practice they are usually 
0029       // integers.  If one or more parameter is ommited in the Python expression 
0030       // that created this slice, than that parameter is None here, and compares 
0031       // equal to a default-constructed boost::python::object.
0032       // If a user-defined type wishes to support slicing, then support for the 
0033       // special meaning associated with negative indices is up to the user.
0034       object start() const;
0035       object stop() const;
0036       object step() const;
0037         
0038    protected:
0039       explicit slice_base(PyObject*, PyObject*, PyObject*);
0040 
0041       BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(slice_base, object)
0042   };
0043 }
0044 
0045 class slice : public detail::slice_base
0046 {
0047     typedef detail::slice_base base;
0048  public:
0049     // Equivalent to slice(::)
0050     slice() : base(0,0,0) {}
0051 
0052     // Each argument must be slice_nil, or implicitly convertable to object.
0053     // They should normally be integers.
0054     template<typename Integer1, typename Integer2>
0055     slice( Integer1 start, Integer2 stop)
0056         : base( object(start).ptr(), object(stop).ptr(), 0 )
0057     {}
0058     
0059     template<typename Integer1, typename Integer2, typename Integer3>
0060     slice( Integer1 start, Integer2 stop, Integer3 stride)
0061         : base( object(start).ptr(), object(stop).ptr(), object(stride).ptr() )
0062     {}
0063         
0064     // The following algorithm is intended to automate the process of 
0065     // determining a slice range when you want to fully support negative
0066     // indices and non-singular step sizes.  Its functionallity is simmilar to 
0067     // PySlice_GetIndicesEx() in the Python/C API, but tailored for C++ users.
0068     // This template returns a slice::range struct that, when used in the 
0069     // following iterative loop, will traverse a slice of the function's
0070     // arguments.
0071     // while (start != end) { 
0072     //     do_foo(...); 
0073     //     std::advance( start, step); 
0074     // }
0075     // do_foo(...); // repeat exactly once more.
0076     
0077     // Arguments: a [begin, end) pair of STL-conforming random-access iterators.
0078         
0079     // Return: slice::range, where start and stop define a _closed_ interval
0080     // that covers at most [begin, end-1] of the provided arguments, and a step 
0081     // that is non-zero.
0082     
0083     // Throws: error_already_set() if any of the indices are neither None nor 
0084     //   integers, or the slice has a step value of zero.
0085     // std::invalid_argument if the resulting range would be empty.  Normally, 
0086     //   you should catch this exception and return an empty sequence of the
0087     //   appropriate type.
0088     
0089     // Performance: constant time for random-access iterators.
0090     
0091     // Rationale: 
0092     //   closed-interval: If an open interval were used, then for a non-singular
0093     //     value for step, the required state for the end iterator could be 
0094     //     beyond the one-past-the-end postion of the specified range.  While 
0095     //     probably harmless, the behavior of STL-conforming iterators is 
0096     //     undefined in this case.
0097     //   exceptions on zero-length range: It is impossible to define a closed 
0098     //     interval over an empty range, so some other form of error checking 
0099     //     would have to be used by the user to prevent undefined behavior.  In
0100     //     the case where the user fails to catch the exception, it will simply
0101     //     be translated to Python by the default exception handling mechanisms.
0102 
0103     template<typename RandomAccessIterator>
0104     struct range
0105     {
0106         RandomAccessIterator start;
0107         RandomAccessIterator stop;
0108         typename iterator_difference<RandomAccessIterator>::type step;
0109     };
0110     
0111     template<typename RandomAccessIterator>
0112     slice::range<RandomAccessIterator>
0113     get_indices( const RandomAccessIterator& begin, 
0114         const RandomAccessIterator& end) const
0115     {
0116         // This is based loosely on PySlice_GetIndicesEx(), but it has been 
0117         // carefully crafted to ensure that these iterators never fall out of
0118         // the range of the container.
0119         slice::range<RandomAccessIterator> ret;
0120         
0121         typedef typename iterator_difference<RandomAccessIterator>::type difference_type;
0122         difference_type max_dist = std::distance(begin, end);
0123 
0124         object slice_start = this->start();
0125         object slice_stop = this->stop();
0126         object slice_step = this->step();
0127         
0128         // Extract the step.
0129         if (slice_step == object()) {
0130             ret.step = 1;
0131         }
0132         else {
0133             ret.step = extract<long>( slice_step);
0134             if (ret.step == 0) {
0135                 PyErr_SetString( PyExc_IndexError, "step size cannot be zero.");
0136                 throw_error_already_set();
0137             }
0138         }
0139         
0140         // Setup the start iterator.
0141         if (slice_start == object()) {
0142             if (ret.step < 0) {
0143                 ret.start = end;
0144                 --ret.start;
0145             }
0146             else
0147                 ret.start = begin;
0148         }
0149         else {
0150             difference_type i = extract<long>( slice_start);
0151             if (i >= max_dist && ret.step > 0)
0152                     throw std::invalid_argument( "Zero-length slice");
0153             if (i >= 0) {
0154                 ret.start = begin;
0155                 BOOST_USING_STD_MIN();
0156                 std::advance( ret.start, min BOOST_PREVENT_MACRO_SUBSTITUTION(i, max_dist-1));
0157             }
0158             else {
0159                 if (i < -max_dist && ret.step < 0)
0160                     throw std::invalid_argument( "Zero-length slice");
0161                 ret.start = end;
0162                 // Advance start (towards begin) not farther than begin.
0163                 std::advance( ret.start, (-i < max_dist) ? i : -max_dist );
0164             }
0165         }
0166         
0167         // Set up the stop iterator.  This one is a little trickier since slices
0168         // define a [) range, and we are returning a [] range.
0169         if (slice_stop == object()) {
0170             if (ret.step < 0) {
0171                 ret.stop = begin;
0172             }
0173             else {
0174                 ret.stop = end;
0175                 std::advance( ret.stop, -1);
0176             }
0177         }
0178         else {
0179             difference_type i = extract<long>(slice_stop);
0180             // First, branch on which direction we are going with this.
0181             if (ret.step < 0) {
0182                 if (i+1 >= max_dist || i == -1)
0183                     throw std::invalid_argument( "Zero-length slice");
0184                 
0185                 if (i >= 0) {
0186                     ret.stop = begin;
0187                     std::advance( ret.stop, i+1);
0188                 }
0189                 else { // i is negative, but more negative than -1.
0190                     ret.stop = end;
0191                     std::advance( ret.stop, (-i < max_dist) ? i : -max_dist);
0192                 }
0193             }
0194             else { // stepping forward
0195                 if (i == 0 || -i >= max_dist)
0196                     throw std::invalid_argument( "Zero-length slice");
0197                 
0198                 if (i > 0) {
0199                     ret.stop = begin;
0200                     std::advance( ret.stop, (std::min)( i-1, max_dist-1));
0201                 }
0202                 else { // i is negative, but not more negative than -max_dist
0203                     ret.stop = end;
0204                     std::advance( ret.stop, i-1);
0205                 }
0206             }
0207         }
0208         
0209         // Now the fun part, handling the possibilites surrounding step.
0210         // At this point, step has been initialized, ret.stop, and ret.step
0211         // represent the widest possible range that could be traveled
0212         // (inclusive), and final_dist is the maximum distance covered by the
0213         // slice.
0214         typename iterator_difference<RandomAccessIterator>::type final_dist = 
0215             std::distance( ret.start, ret.stop);
0216         
0217         // First case, if both ret.start and ret.stop are equal, then step
0218         // is irrelevant and we can return here.
0219         if (final_dist == 0)
0220             return ret;
0221         
0222         // Second, if there is a sign mismatch, than the resulting range and 
0223         // step size conflict: std::advance( ret.start, ret.step) goes away from
0224         // ret.stop.
0225         if ((final_dist > 0) != (ret.step > 0))
0226             throw std::invalid_argument( "Zero-length slice.");
0227         
0228         // Finally, if the last step puts us past the end, we move ret.stop
0229         // towards ret.start in the amount of the remainder.
0230         // I don't remember all of the oolies surrounding negative modulii,
0231         // so I am handling each of these cases separately.
0232         if (final_dist < 0) {
0233             difference_type remainder = -final_dist % -ret.step;
0234             std::advance( ret.stop, remainder);
0235         }
0236         else {
0237             difference_type remainder = final_dist % ret.step;
0238             std::advance( ret.stop, -remainder);
0239         }
0240         
0241         return ret;
0242     }
0243 
0244     // Incorrect spelling. DO NOT USE. Only here for backward compatibility.
0245     // Corrected 2011-06-14.
0246     template<typename RandomAccessIterator>
0247     slice::range<RandomAccessIterator>
0248     get_indicies( const RandomAccessIterator& begin, 
0249         const RandomAccessIterator& end) const
0250     {
0251         return get_indices(begin, end);
0252     }
0253         
0254  public:
0255     // This declaration, in conjunction with the specialization of 
0256     // object_manager_traits<> below, allows C++ functions accepting slice 
0257     // arguments to be called from from Python.  These constructors should never
0258     // be used in client code.
0259     BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(slice, detail::slice_base)
0260 };
0261 
0262 
0263 namespace converter {
0264 
0265 template<>
0266 struct object_manager_traits<slice>
0267     : pytype_object_manager_traits<&PySlice_Type, slice>
0268 {
0269 };
0270     
0271 } // !namesapce converter
0272 
0273 } } // !namespace ::boost::python
0274 
0275 
0276 #endif // !defined BOOST_PYTHON_SLICE_JDB20040105_HPP