mirror of
https://github.com/boostorg/python.git
synced 2026-01-20 16:52:15 +00:00
Remove slice to separate file, out of line function bodies, working set_slice
[SVN r1528]
This commit is contained in:
@@ -26,81 +26,29 @@
|
||||
|
||||
#include <boost/python/object.hpp>
|
||||
#include <boost/python/list.hpp>
|
||||
#include <boost/python/converter/pytype_object_mgr_traits.hpp>
|
||||
#include <boost/python/extract.hpp>
|
||||
#include <boost/mpl/apply.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
// #include <boost/python/extract.hpp>
|
||||
#include "slice.hpp"
|
||||
#include "python_iterator.hpp"
|
||||
|
||||
namespace indexing
|
||||
{
|
||||
class slice : public boost::python::object
|
||||
{
|
||||
int mStart;
|
||||
int mStep;
|
||||
int mStop;
|
||||
bool mLengthSet;
|
||||
|
||||
void validate () const {
|
||||
if (!mLengthSet)
|
||||
{
|
||||
PyErr_SetString (PyExc_RuntimeError
|
||||
, "slice access attempted before setLength called");
|
||||
boost::python::throw_error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
slice (boost::python::detail::borrowed_reference ref)
|
||||
: boost::python::object (ref)
|
||||
, mStart (0)
|
||||
, mStep (0)
|
||||
, mStop (0)
|
||||
, mLengthSet (false)
|
||||
{
|
||||
if (!PySlice_Check (this->ptr()))
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError
|
||||
, "slice constructor: passed a non-slice object");
|
||||
|
||||
boost::python::throw_error_already_set();
|
||||
}
|
||||
|
||||
//
|
||||
// *** WARNING ***
|
||||
//
|
||||
// The slice object is useless until setLength is called
|
||||
//
|
||||
}
|
||||
|
||||
void setLength (int sequenceLength)
|
||||
{
|
||||
PySlice_GetIndices ((PySliceObject *) this->ptr()
|
||||
, sequenceLength
|
||||
, &mStart
|
||||
, &mStop
|
||||
, &mStep);
|
||||
|
||||
mStart = std::max (0, std::min (sequenceLength, mStart));
|
||||
mStop = std::max (0, std::min (sequenceLength, mStop));
|
||||
|
||||
mLengthSet = true;
|
||||
}
|
||||
|
||||
int start() const { validate(); return mStart; }
|
||||
int step() const { validate(); return mStep; }
|
||||
int stop() const { validate(); return mStop; }
|
||||
};
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
struct slice_handler
|
||||
{
|
||||
static boost::python::object make_getitem (Policy const &);
|
||||
static boost::python::object make_setitem (Policy const &);
|
||||
|
||||
private:
|
||||
typedef typename Algorithms::container container;
|
||||
typedef typename Algorithms::index_param index_param;
|
||||
typedef typename Algorithms::value_type value_type;
|
||||
typedef typename Algorithms::reference reference;
|
||||
|
||||
class postcall_override
|
||||
static boost::python::list get_slice (container &, slice);
|
||||
static void set_slice (container &, slice, boost::python::object);
|
||||
|
||||
struct postcall_override
|
||||
{
|
||||
// This class overrides our Policy's postcall function and
|
||||
// result_conveter to handle the list returned from get_slice.
|
||||
@@ -109,69 +57,240 @@ namespace indexing
|
||||
// original postcall to each element of the Python list returned
|
||||
// from get_slice.
|
||||
|
||||
Policy mBase;
|
||||
|
||||
public:
|
||||
postcall_override (Policy const &p) : mBase (p) {
|
||||
}
|
||||
|
||||
bool precall (PyObject *args) {
|
||||
return mBase.precall (args);
|
||||
}
|
||||
|
||||
PyObject* postcall (PyObject *args, PyObject *result) {
|
||||
int size = PyList_Size (result);
|
||||
|
||||
for (int count = 0; count < size; ++count)
|
||||
{
|
||||
mBase.postcall (args, PyList_GetItem (result, count));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef boost::python::default_result_converter result_converter;
|
||||
|
||||
postcall_override (Policy const &p);
|
||||
|
||||
bool precall (PyObject *args);
|
||||
PyObject* postcall (PyObject *args, PyObject *result);
|
||||
|
||||
private:
|
||||
Policy mBase;
|
||||
};
|
||||
};
|
||||
|
||||
static boost::python::list get_slice (container &c, slice sl)
|
||||
template<bool doit> struct maybe_insert {
|
||||
template<class Algorithms>
|
||||
static void apply (typename Algorithms::container &
|
||||
, typename Algorithms::index_param
|
||||
, typename Algorithms::value_param)
|
||||
{
|
||||
typedef typename Policy::result_converter converter_type;
|
||||
typedef typename Algorithms::reference reference;
|
||||
typename boost::mpl::apply1<converter_type, reference>::type converter;
|
||||
PyErr_SetString (PyExc_TypeError
|
||||
, "container does not support item insertion");
|
||||
|
||||
boost::python::list result;
|
||||
|
||||
sl.setLength (Algorithms::size(c));
|
||||
|
||||
int direction = (sl.step() > 0) ? 1 : ((sl.step() == 0) ? 0 : -1);
|
||||
|
||||
for (int index = sl.start()
|
||||
; ((sl.stop() - index) * direction) > 0
|
||||
; index += sl.step())
|
||||
{
|
||||
result.append
|
||||
(boost::python::handle<>
|
||||
(converter (Algorithms::get (c, index))));
|
||||
}
|
||||
|
||||
return result;
|
||||
boost::python::throw_error_already_set ();
|
||||
}
|
||||
};
|
||||
|
||||
static boost::python::object make_getitem (Policy const &policy)
|
||||
template<> struct maybe_insert<true> {
|
||||
template<class Algorithms>
|
||||
static void apply (typename Algorithms::container &c
|
||||
, typename Algorithms::index_param i
|
||||
, typename Algorithms::value_param v)
|
||||
{
|
||||
return boost::python::make_function
|
||||
(get_slice, postcall_override (policy));
|
||||
Algorithms::insert (c, i, v);
|
||||
}
|
||||
};
|
||||
|
||||
template<bool doit> struct maybe_erase {
|
||||
template<class Algorithms>
|
||||
static void apply (typename Algorithms::container &
|
||||
, typename Algorithms::index_param
|
||||
, typename Algorithms::index_param)
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError
|
||||
, "container does not support item deletion");
|
||||
|
||||
boost::python::throw_error_already_set ();
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct maybe_erase<true> {
|
||||
template<class Algorithms>
|
||||
static void apply (typename Algorithms::container &c
|
||||
, typename Algorithms::index_param from
|
||||
, typename Algorithms::index_param to)
|
||||
{
|
||||
Algorithms::erase (c, from, to);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace boost { namespace python { namespace converter {
|
||||
// Specialized converter to handle PySlice_Type objects
|
||||
template<>
|
||||
struct object_manager_traits<indexing::slice>
|
||||
: pytype_object_manager_traits<&PySlice_Type, ::indexing::slice>
|
||||
{
|
||||
};
|
||||
}}}
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// postcall_override constructor
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
indexing::slice_handler<Algorithms, Policy>
|
||||
::postcall_override::postcall_override (Policy const &p)
|
||||
: mBase (p)
|
||||
{
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// precall forwarder
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
bool
|
||||
indexing::slice_handler<Algorithms, Policy>
|
||||
::postcall_override::precall (PyObject *args)
|
||||
{
|
||||
return mBase.precall (args);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Apply base postcall to each element of the list returend by get_slice
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
PyObject *
|
||||
indexing::slice_handler<Algorithms, Policy>
|
||||
::postcall_override::postcall (PyObject *args, PyObject *result)
|
||||
{
|
||||
int size = PyList_Size (result);
|
||||
|
||||
for (int count = 0; count < size; ++count)
|
||||
{
|
||||
mBase.postcall (args, PyList_GetItem (result, count));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Return a function object that implements the slice version of __getitem__
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
boost::python::object
|
||||
indexing::slice_handler<Algorithms, Policy>
|
||||
::make_getitem (Policy const &policy)
|
||||
{
|
||||
return boost::python::make_function (get_slice, postcall_override (policy));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Return a function object that implements the slice version of __setitem__
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
boost::python::object
|
||||
indexing::slice_handler<Algorithms, Policy>
|
||||
::make_setitem (Policy const &policy)
|
||||
{
|
||||
// should we try to get funky with policy::precall?
|
||||
return boost::python::make_function (set_slice, policy);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation for the slice version of __getitem__
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
boost::python::list
|
||||
indexing::slice_handler<Algorithms, Policy>
|
||||
::get_slice (container &c, slice sl)
|
||||
{
|
||||
typedef typename Policy::result_converter converter_type;
|
||||
typedef typename Algorithms::reference reference;
|
||||
typename boost::mpl::apply1<converter_type, reference>::type converter;
|
||||
|
||||
boost::python::list result;
|
||||
|
||||
sl.setLength (Algorithms::size(c));
|
||||
|
||||
for (int index = sl.start(); sl.inRange (index); index += sl.step())
|
||||
{
|
||||
// Apply the result converter (only) to each element before
|
||||
// appending. postcall is done in postcall_override
|
||||
|
||||
result.append
|
||||
(boost::python::handle<>
|
||||
(converter (Algorithms::get (c, index))));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation for the slice version of __setitem__
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Algorithms, class Policy>
|
||||
void
|
||||
indexing::slice_handler<Algorithms, Policy>
|
||||
::set_slice (container &c, slice sl, boost::python::object values)
|
||||
{
|
||||
std::auto_ptr<python_iterator> iterPtr (make_iterator (values));
|
||||
|
||||
if (!iterPtr.get())
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError
|
||||
, "Type assigned to slice must be a sequence");
|
||||
|
||||
boost::python::throw_error_already_set();
|
||||
}
|
||||
|
||||
typedef typename Algorithms::container_traits traits;
|
||||
typedef boost::python::extract<typename Algorithms::value_param> extractor;
|
||||
|
||||
// Note: any error during this operation will probably leave the
|
||||
// container partially updated. This can occur (for example) if the
|
||||
// replacement sequence is of a different length to the original
|
||||
// slice and the container does not support insertion/deletion.
|
||||
// This could be prevented if the length of the replacement sequence
|
||||
// is known in advance (via __len__, for example) but not otherwise.
|
||||
|
||||
sl.setLength (Algorithms::size (c)); // Current length of our container
|
||||
int index = sl.start(); // Index in our container for update
|
||||
|
||||
// Overwrite and/or insert elements
|
||||
while (iterPtr->next())
|
||||
{
|
||||
if (sl.inRange (index))
|
||||
{
|
||||
Algorithms::assign (c, index, extractor (iterPtr->current()));
|
||||
}
|
||||
|
||||
else if (sl.step() != 1)
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError
|
||||
, "attempt to insert via extended slice");
|
||||
|
||||
boost::python::throw_error_already_set ();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Could optimize this in some cases (i.e. if the length of
|
||||
// the replacement sequence is known)
|
||||
|
||||
maybe_insert<traits::has_insert>
|
||||
::template apply<Algorithms> (c, index
|
||||
, extractor (iterPtr->current()));
|
||||
}
|
||||
|
||||
index += sl.step();
|
||||
}
|
||||
|
||||
// Erase any remaining elements in the slice
|
||||
if (sl.inRange(index))
|
||||
{
|
||||
if (sl.step() != 1)
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError
|
||||
, "attempt to erase via extended slice");
|
||||
|
||||
boost::python::throw_error_already_set ();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
maybe_erase<traits::has_erase>
|
||||
::template apply<Algorithms> (c, index, sl.stop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // slice_handler_rmg_20030909_included
|
||||
|
||||
Reference in New Issue
Block a user