diff options
Diffstat (limited to 'boost/python/suite/indexing')
-rw-r--r-- | boost/python/suite/indexing/container_utils.hpp | 57 | ||||
-rw-r--r-- | boost/python/suite/indexing/detail/indexing_suite_detail.hpp | 759 | ||||
-rw-r--r-- | boost/python/suite/indexing/indexing_suite.hpp | 299 | ||||
-rw-r--r-- | boost/python/suite/indexing/map_indexing_suite.hpp | 181 | ||||
-rw-r--r-- | boost/python/suite/indexing/vector_indexing_suite.hpp | 242 |
5 files changed, 1538 insertions, 0 deletions
diff --git a/boost/python/suite/indexing/container_utils.hpp b/boost/python/suite/indexing/container_utils.hpp new file mode 100644 index 0000000000..72710bb8b0 --- /dev/null +++ b/boost/python/suite/indexing/container_utils.hpp @@ -0,0 +1,57 @@ + +// (C) Copyright Joel de Guzman 2003. +// 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 PY_CONTAINER_UTILS_JDG20038_HPP +# define PY_CONTAINER_UTILS_JDG20038_HPP + +# include <utility> +# include <boost/foreach.hpp> +# include <boost/python/object.hpp> +# include <boost/python/handle.hpp> +# include <boost/python/extract.hpp> +# include <boost/python/stl_iterator.hpp> + +namespace boost { namespace python { namespace container_utils { + + template <typename Container> + void + extend_container(Container& container, object l) + { + typedef typename Container::value_type data_type; + + // l must be iterable + BOOST_FOREACH(object elem, + std::make_pair( + boost::python::stl_input_iterator<object>(l), + boost::python::stl_input_iterator<object>() + )) + { + extract<data_type const&> x(elem); + // try if elem is an exact data_type type + if (x.check()) + { + container.push_back(x()); + } + else + { + // try to convert elem to data_type type + extract<data_type> x(elem); + if (x.check()) + { + container.push_back(x()); + } + else + { + PyErr_SetString(PyExc_TypeError, "Incompatible Data Type"); + throw_error_already_set(); + } + } + } + } + +}}} // namespace boost::python::container_utils + +#endif diff --git a/boost/python/suite/indexing/detail/indexing_suite_detail.hpp b/boost/python/suite/indexing/detail/indexing_suite_detail.hpp new file mode 100644 index 0000000000..70df8a7273 --- /dev/null +++ b/boost/python/suite/indexing/detail/indexing_suite_detail.hpp @@ -0,0 +1,759 @@ +// (C) Copyright Joel de Guzman 2003. +// 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 INDEXING_SUITE_DETAIL_JDG20036_HPP +# define INDEXING_SUITE_DETAIL_JDG20036_HPP + +# include <boost/python/extract.hpp> +# include <boost/scoped_ptr.hpp> +# include <boost/get_pointer.hpp> +# include <boost/detail/binary_search.hpp> +# include <boost/numeric/conversion/cast.hpp> +# include <boost/type_traits/is_pointer.hpp> +# include <vector> +# include <map> +#include <iostream> + +namespace boost { namespace python { namespace detail { + +#if defined(NDEBUG) +#define BOOST_PYTHON_INDEXING_CHECK_INVARIANT +#else +#define BOOST_PYTHON_INDEXING_CHECK_INVARIANT check_invariant() +#endif + + template <class Proxy> + struct compare_proxy_index + { + // This functor compares a proxy and an index. + // This is used by proxy_group::first_proxy to + // get first proxy with index i. + + template <class Index> + bool operator()(PyObject* prox, Index i) const + { + typedef typename Proxy::policies_type policies_type; + Proxy& proxy = extract<Proxy&>(prox)(); + return policies_type:: + compare_index(proxy.get_container(), proxy.get_index(), i); + } + }; + + // The proxy_group class holds a vector of container element + // proxies. First, what is a container element proxy? A container + // element proxy acts like a smart pointer holding a reference to + // a container and an index (see container_element, for details). + // + // The proxies are held in a vector always sorted by its index. + // Various functions manage the addition, removal and searching + // of proxies from the vector. + // + template <class Proxy> + class proxy_group + { + public: + + typedef typename std::vector<PyObject*>::const_iterator const_iterator; + typedef typename std::vector<PyObject*>::iterator iterator; + typedef typename Proxy::index_type index_type; + typedef typename Proxy::policies_type policies_type; + + iterator + first_proxy(index_type i) + { + // Return the first proxy with index <= i + return boost::detail::lower_bound( + proxies.begin(), proxies.end(), + i, compare_proxy_index<Proxy>()); + } + + void + remove(Proxy& proxy) + { + // Remove a proxy + for (iterator iter = first_proxy(proxy.get_index()); + iter != proxies.end(); ++iter) + { + if (&extract<Proxy&>(*iter)() == &proxy) + { + proxies.erase(iter); + break; + } + } + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + } + + void + add(PyObject* prox) + { + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + // Add a proxy + proxies.insert( + first_proxy(extract<Proxy&>(prox)().get_index()), prox); + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + } + + void + erase(index_type i, mpl::false_) + { + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + // Erase the proxy with index i + replace(i, i+1, 0); + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + } + + void + erase(index_type i, mpl::true_) + { + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + // Erase the proxy with index i + + iterator iter = first_proxy(i); + extract<Proxy&> p(*iter); + + if (iter != proxies.end() && p().get_index() == i) + { + extract<Proxy&> p(*iter); + p().detach(); + proxies.erase(iter); + } + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + } + + void + erase(index_type from, index_type to) + { + // note: this cannot be called when container is not sliceable + + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + // Erase all proxies with indexes from..to + replace(from, to, 0); + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + } + + void + replace( + index_type from, + index_type to, + typename std::vector<PyObject*>::size_type len) + { + // note: this cannot be called when container is not sliceable + + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + // Erase all proxies with indexes from..to. + // Adjust the displaced indexes such that the + // final effect is that we have inserted *len* + // number of proxies in the vacated region. This + // procedure involves adjusting the indexes of + // the proxies. + + iterator left = first_proxy(from); + iterator right = proxies.end(); // we'll adjust this later + + for (iterator iter = left; iter != right; ++iter) + { + if (extract<Proxy&>(*iter)().get_index() > to) + { + right = iter; // adjust right + break; + } + extract<Proxy&> p(*iter); + p().detach(); + } + + typename std::vector<PyObject*>::size_type + offset = left-proxies.begin(); + proxies.erase(left, right); + right = proxies.begin()+offset; + + while (right != proxies.end()) + { + typedef typename Proxy::container_type::difference_type difference_type; + extract<Proxy&> p(*right); + p().set_index( + extract<Proxy&>(*right)().get_index() + - (difference_type(to) - from - len) + ); + + ++right; + } + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + } + + PyObject* + find(index_type i) + { + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + // Find the proxy with *exact* index i. + // Return 0 (null) if no proxy with the + // given index is found. + iterator iter = first_proxy(i); + if (iter != proxies.end() + && extract<Proxy&>(*iter)().get_index() == i) + { + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + return *iter; + } + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + return 0; + } + + typename std::vector<PyObject*>::size_type + size() const + { + BOOST_PYTHON_INDEXING_CHECK_INVARIANT; + // How many proxies are there so far? + return proxies.size(); + } + + private: + +#if !defined(NDEBUG) + void + check_invariant() const + { + for (const_iterator i = proxies.begin(); i != proxies.end(); ++i) + { + if ((*i)->ob_refcnt <= 0) + { + PyErr_SetString(PyExc_RuntimeError, + "Invariant: Proxy vector in an inconsistent state"); + throw_error_already_set(); + } + + if (i+1 != proxies.end()) + { + if (extract<Proxy&>(*(i+1))().get_index() == + extract<Proxy&>(*(i))().get_index()) + { + PyErr_SetString(PyExc_RuntimeError, + "Invariant: Proxy vector in an inconsistent state (duplicate proxy)"); + throw_error_already_set(); + } + } + } + } +#endif + + std::vector<PyObject*> proxies; + }; + + // proxy_links holds a map of Container pointers (keys) + // with proxy_group(s) (data). Various functions manage + // the addition, removal and searching of proxies from + // the map. + // + template <class Proxy, class Container> + class proxy_links + { + public: + + typedef std::map<Container*, proxy_group<Proxy> > links_t; + typedef typename Proxy::index_type index_type; + + void + remove(Proxy& proxy) + { + // Remove a proxy. + typename links_t::iterator r = links.find(&proxy.get_container()); + if (r != links.end()) + { + r->second.remove(proxy); + if (r->second.size() == 0) + links.erase(r); + } + } + + void + add(PyObject* prox, Container& container) + { + // Add a proxy + links[&container].add(prox); + } + + template <class NoSlice> + void erase(Container& container, index_type i, NoSlice no_slice) + { + // Erase the proxy with index i + typename links_t::iterator r = links.find(&container); + if (r != links.end()) + { + r->second.erase(i, no_slice); + if (r->second.size() == 0) + links.erase(r); + } + } + + void + erase(Container& container, index_type from, index_type to) + { + // Erase all proxies with indexes from..to + typename links_t::iterator r = links.find(&container); + if (r != links.end()) + { + r->second.erase(from, to); + if (r->second.size() == 0) + links.erase(r); + } + } + + void + replace( + Container& container, + index_type from, index_type to, index_type len) + { + // Erase all proxies with indexes from..to. + // Adjust the displaced indexes such that the + // final effect is that we have inserted *len* + // number of proxies in the vacated region. This + // procedure involves adjusting the indexes of + // the proxies. + + typename links_t::iterator r = links.find(&container); + if (r != links.end()) + { + r->second.replace(from, to, len); + if (r->second.size() == 0) + links.erase(r); + } + } + + PyObject* + find(Container& container, index_type i) + { + // Find the proxy with *exact* index i. + // Return 0 (null) if no proxy with the given + // index is found. + typename links_t::iterator r = links.find(&container); + if (r != links.end()) + return r->second.find(i); + return 0; + } + + private: + + links_t links; + }; + + // container_element is our container proxy class. + // This class acts like a smart pointer to a container + // element. The class holds an index and a reference to + // a container. Dereferencing the smart pointer will + // retrieve the nth (index) element from the container. + // + // A container_element can also be detached from the + // container. In such a detached state, the container_element + // holds a copy of the nth (index) element, which it + // returns when dereferenced. + // + template <class Container, class Index, class Policies> + class container_element + { + public: + + typedef Index index_type; + typedef Container container_type; + typedef typename Policies::data_type element_type; + typedef Policies policies_type; + typedef container_element<Container, Index, Policies> self_t; + typedef proxy_group<self_t> links_type; + + container_element(object container, Index index) + : ptr() + , container(container) + , index(index) + { + } + + container_element(container_element const& ce) + : ptr(ce.ptr.get() == 0 ? 0 : new element_type(*ce.ptr.get())) + , container(ce.container) + , index(ce.index) + { + } + + ~container_element() + { + if (!is_detached()) + get_links().remove(*this); + } + + element_type& operator*() const + { + if (is_detached()) + return *get_pointer(ptr); + return Policies::get_item(get_container(), index); + } + + element_type* get() const + { + if (is_detached()) + return get_pointer(ptr); + return &Policies::get_item(get_container(), index); + } + + void + detach() + { + if (!is_detached()) + { + ptr.reset( + new element_type( + Policies::get_item(get_container(), index))); + container = object(); // free container. reset it to None + } + } + + bool + is_detached() const + { + return get_pointer(ptr) != 0; + } + + Container& + get_container() const + { + return extract<Container&>(container)(); + } + + Index + get_index() const + { + return index; + } + + void + set_index(Index i) + { + index = i; + } + + static proxy_links<self_t, Container>& + get_links() + { + // All container_element(s) maintain links to + // its container in a global map (see proxy_links). + // This global "links" map is a singleton. + + static proxy_links<self_t, Container> links; + return links; // singleton + } + + private: + + container_element& operator=(container_element const& ce); + + scoped_ptr<element_type> ptr; + object container; + Index index; + }; + + template < + class Container + , class DerivedPolicies + , class ContainerElement + , class Index + > + struct no_proxy_helper + { + static void + register_container_element() + { + } + + template <class DataType> + static object + base_get_item_helper(DataType const& p, mpl::true_) + { + return object(ptr(p)); + } + + template <class DataType> + static object + base_get_item_helper(DataType const& x, mpl::false_) + { + return object(x); + } + + static object + base_get_item_(back_reference<Container&> const& container, PyObject* i) + { + return base_get_item_helper( + DerivedPolicies::get_item( + container.get(), DerivedPolicies:: + convert_index(container.get(), i)) + , is_pointer<BOOST_DEDUCED_TYPENAME Container::value_type>() + ); + } + + static void + base_replace_indexes( + Container& /*container*/, Index /*from*/, + Index /*to*/, Index /*n*/) + { + } + + template <class NoSlice> + static void + base_erase_index( + Container& /*container*/, Index /*i*/, NoSlice /*no_slice*/) + { + } + + static void + base_erase_indexes(Container& /*container*/, Index /*from*/, Index /*to*/) + { + } + }; + + template < + class Container + , class DerivedPolicies + , class ContainerElement + , class Index + > + struct proxy_helper + { + static void + register_container_element() + { + register_ptr_to_python<ContainerElement>(); + } + + static object + base_get_item_(back_reference<Container&> const& container, PyObject* i) + { + // Proxy + Index idx = DerivedPolicies::convert_index(container.get(), i); + + if (PyObject* shared = + ContainerElement::get_links().find(container.get(), idx)) + { + handle<> h(python::borrowed(shared)); + return object(h); + } + else + { + object prox(ContainerElement(container.source(), idx)); + ContainerElement:: + get_links().add(prox.ptr(), container.get()); + return prox; + } + } + + static void + base_replace_indexes( + Container& container, Index from, + Index to, Index n) + { + ContainerElement::get_links().replace(container, from, to, n); + } + + template <class NoSlice> + static void + base_erase_index( + Container& container, Index i, NoSlice no_slice) + { + ContainerElement::get_links().erase(container, i, no_slice); + } + + static void + base_erase_indexes( + Container& container, Index from, Index to) + { + ContainerElement::get_links().erase(container, from, to); + } + }; + + template < + class Container + , class DerivedPolicies + , class ProxyHandler + , class Data + , class Index + > + struct slice_helper + { + static object + base_get_slice(Container& container, PySliceObject* slice) + { + Index from, to; + base_get_slice_data(container, slice, from, to); + return DerivedPolicies::get_slice(container, from, to); + } + + static void + base_get_slice_data( + Container& container, PySliceObject* slice, Index& from_, Index& to_) + { + if (Py_None != slice->step) { + PyErr_SetString( PyExc_IndexError, "slice step size not supported."); + throw_error_already_set(); + } + + Index min_index = DerivedPolicies::get_min_index(container); + Index max_index = DerivedPolicies::get_max_index(container); + + if (Py_None == slice->start) { + from_ = min_index; + } + else { + long from = extract<long>( slice->start); + if (from < 0) // Negative slice index + from += max_index; + if (from < 0) // Clip lower bounds to zero + from = 0; + from_ = boost::numeric_cast<Index>(from); + if (from_ > max_index) // Clip upper bounds to max_index. + from_ = max_index; + } + + if (Py_None == slice->stop) { + to_ = max_index; + } + else { + long to = extract<long>( slice->stop); + if (to < 0) + to += max_index; + if (to < 0) + to = 0; + to_ = boost::numeric_cast<Index>(to); + if (to_ > max_index) + to_ = max_index; + } + } + + static void + base_set_slice(Container& container, PySliceObject* slice, PyObject* v) + { + Index from, to; + base_get_slice_data(container, slice, from, to); + + extract<Data&> elem(v); + // try if elem is an exact Data + if (elem.check()) + { + ProxyHandler::base_replace_indexes(container, from, to, 1); + DerivedPolicies::set_slice(container, from, to, elem()); + } + else + { + // try to convert elem to Data + extract<Data> elem(v); + if (elem.check()) + { + ProxyHandler::base_replace_indexes(container, from, to, 1); + DerivedPolicies::set_slice(container, from, to, elem()); + } + else + { + // Otherwise, it must be a list or some container + handle<> l_(python::borrowed(v)); + object l(l_); + + std::vector<Data> temp; + for (int i = 0; i < l.attr("__len__")(); i++) + { + object elem(l[i]); + extract<Data const&> x(elem); + // try if elem is an exact Data type + if (x.check()) + { + temp.push_back(x()); + } + else + { + // try to convert elem to Data type + extract<Data> x(elem); + if (x.check()) + { + temp.push_back(x()); + } + else + { + PyErr_SetString(PyExc_TypeError, + "Invalid sequence element"); + throw_error_already_set(); + } + } + } + + ProxyHandler::base_replace_indexes(container, from, to, + temp.end()-temp.begin()); + DerivedPolicies::set_slice(container, from, to, + temp.begin(), temp.end()); + } + } + } + + static void + base_delete_slice(Container& container, PySliceObject* slice) + { + Index from, to; + base_get_slice_data(container, slice, from, to); + ProxyHandler::base_erase_indexes(container, from, to); + DerivedPolicies::delete_slice(container, from, to); + } + }; + + template < + class Container + , class DerivedPolicies + , class ProxyHandler + , class Data + , class Index + > + struct no_slice_helper + { + static void + slicing_not_suported() + { + PyErr_SetString(PyExc_RuntimeError, "Slicing not supported"); + throw_error_already_set(); + } + + static object + base_get_slice(Container& /*container*/, PySliceObject* /*slice*/) + { + slicing_not_suported(); + return object(); + } + + static void + base_set_slice(Container& /*container*/, PySliceObject* /*slice*/, PyObject* /*v*/) + { + slicing_not_suported(); + } + + static void + base_delete_slice(Container& /*container*/, PySliceObject* /*slice*/) + { + slicing_not_suported(); + } + }; + +#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +}} // namespace python::detail +#endif + + template <class Container, class Index, class Policies> + inline typename Policies::data_type* + get_pointer( + python::detail::container_element<Container, Index, Policies> const& p) + { + // Get the pointer of a container_element smart pointer + return p.get(); + } + +#ifndef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP + // Don't hide these other get_pointer overloads + using boost::python::get_pointer; + using boost::get_pointer; +}} // namespace python::detail +#endif + +} // namespace boost + +#endif // INDEXING_SUITE_DETAIL_JDG20036_HPP diff --git a/boost/python/suite/indexing/indexing_suite.hpp b/boost/python/suite/indexing/indexing_suite.hpp new file mode 100644 index 0000000000..b636b2111c --- /dev/null +++ b/boost/python/suite/indexing/indexing_suite.hpp @@ -0,0 +1,299 @@ +// (C) Copyright Joel de Guzman 2003. +// 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 INDEXING_SUITE_JDG20036_HPP +# define INDEXING_SUITE_JDG20036_HPP + +# include <boost/python/class.hpp> +# include <boost/python/def_visitor.hpp> +# include <boost/python/register_ptr_to_python.hpp> +# include <boost/python/suite/indexing/detail/indexing_suite_detail.hpp> +# include <boost/python/return_internal_reference.hpp> +# include <boost/python/iterator.hpp> +# include <boost/mpl/or.hpp> +# include <boost/mpl/not.hpp> +# include <boost/type_traits/is_same.hpp> + +namespace boost { namespace python { + + // indexing_suite class. This class is the facade class for + // the management of C++ containers intended to be integrated + // to Python. The objective is make a C++ container look and + // feel and behave exactly as we'd expect a Python container. + // By default indexed elements are returned by proxy. This can be + // disabled by supplying *true* in the NoProxy template parameter. + // + // Derived classes provide the hooks needed by the indexing_suite + // to do its job: + // + // static data_type& + // get_item(Container& container, index_type i); + // + // static object + // get_slice(Container& container, index_type from, index_type to); + // + // static void + // set_item(Container& container, index_type i, data_type const& v); + // + // static void + // set_slice( + // Container& container, index_type from, + // index_type to, data_type const& v + // ); + // + // template <class Iter> + // static void + // set_slice(Container& container, index_type from, + // index_type to, Iter first, Iter last + // ); + // + // static void + // delete_item(Container& container, index_type i); + // + // static void + // delete_slice(Container& container, index_type from, index_type to); + // + // static size_t + // size(Container& container); + // + // template <class T> + // static bool + // contains(Container& container, T const& val); + // + // static index_type + // convert_index(Container& container, PyObject* i); + // + // static index_type + // adjust_index(index_type current, index_type from, + // index_type to, size_type len + // ); + // + // Most of these policies are self explanatory. convert_index and + // adjust_index, however, deserves some explanation. + // + // convert_index converts an Python index into a C++ index that the + // container can handle. For instance, negative indexes in Python, by + // convention, indexes from the right (e.g. C[-1] indexes the rightmost + // element in C). convert_index should handle the necessary conversion + // for the C++ container (e.g. convert -1 to C.size()-1). convert_index + // should also be able to convert the type of the index (A dynamic Python + // type) to the actual type that the C++ container expects. + // + // When a container expands or contracts, held indexes to its elements + // must be adjusted to follow the movement of data. For instance, if + // we erase 3 elements, starting from index 0 from a 5 element vector, + // what used to be at index 4 will now be at index 1: + // + // [a][b][c][d][e] ---> [d][e] + // ^ ^ + // 4 1 + // + // adjust_index takes care of the adjustment. Given a current index, + // the function should return the adjusted index when data in the + // container at index from..to is replaced by *len* elements. + // + + template < + class Container + , class DerivedPolicies + , bool NoProxy = false + , bool NoSlice = false + , class Data = typename Container::value_type + , class Index = typename Container::size_type + , class Key = typename Container::value_type + > + class indexing_suite + : public def_visitor< + indexing_suite< + Container + , DerivedPolicies + , NoProxy + , NoSlice + , Data + , Index + , Key + > > + { + private: + + typedef mpl::or_< + mpl::bool_<NoProxy> + , mpl::not_<is_class<Data> > + , typename mpl::or_< + is_same<Data, std::string> + , is_same<Data, std::complex<float> > + , is_same<Data, std::complex<double> > + , is_same<Data, std::complex<long double> > >::type> + no_proxy; + + typedef detail::container_element<Container, Index, DerivedPolicies> + container_element_t; + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + struct return_policy : return_internal_reference<> {}; +#else + typedef return_internal_reference<> return_policy; +#endif + + typedef typename mpl::if_< + no_proxy + , iterator<Container> + , iterator<Container, return_policy> >::type + def_iterator; + + typedef typename mpl::if_< + no_proxy + , detail::no_proxy_helper< + Container + , DerivedPolicies + , container_element_t + , Index> + , detail::proxy_helper< + Container + , DerivedPolicies + , container_element_t + , Index> >::type + proxy_handler; + + typedef typename mpl::if_< + mpl::bool_<NoSlice> + , detail::no_slice_helper< + Container + , DerivedPolicies + , proxy_handler + , Data + , Index> + , detail::slice_helper< + Container + , DerivedPolicies + , proxy_handler + , Data + , Index> >::type + slice_handler; + + public: + + template <class Class> + void visit(Class& cl) const + { + // Hook into the class_ generic visitation .def function + proxy_handler::register_container_element(); + + cl + .def("__len__", base_size) + .def("__setitem__", &base_set_item) + .def("__delitem__", &base_delete_item) + .def("__getitem__", &base_get_item) + .def("__contains__", &base_contains) + .def("__iter__", def_iterator()) + ; + + DerivedPolicies::extension_def(cl); + } + + template <class Class> + static void + extension_def(Class& cl) + { + // default. + // no more extensions + } + + private: + + static object + base_get_item(back_reference<Container&> container, PyObject* i) + { + if (PySlice_Check(i)) + return slice_handler::base_get_slice( + container.get(), static_cast<PySliceObject*>(static_cast<void*>(i))); + + return proxy_handler::base_get_item_(container, i); + } + + static void + base_set_item(Container& container, PyObject* i, PyObject* v) + { + if (PySlice_Check(i)) + { + slice_handler::base_set_slice(container, + static_cast<PySliceObject*>(static_cast<void*>(i)), v); + } + else + { + extract<Data&> elem(v); + // try if elem is an exact Data + if (elem.check()) + { + DerivedPolicies:: + set_item(container, + DerivedPolicies:: + convert_index(container, i), elem()); + } + else + { + // try to convert elem to Data + extract<Data> elem(v); + if (elem.check()) + { + DerivedPolicies:: + set_item(container, + DerivedPolicies:: + convert_index(container, i), elem()); + } + else + { + PyErr_SetString(PyExc_TypeError, "Invalid assignment"); + throw_error_already_set(); + } + } + } + } + + static void + base_delete_item(Container& container, PyObject* i) + { + if (PySlice_Check(i)) + { + slice_handler::base_delete_slice( + container, static_cast<PySliceObject*>(static_cast<void*>(i))); + return; + } + + Index index = DerivedPolicies::convert_index(container, i); + proxy_handler::base_erase_index(container, index, mpl::bool_<NoSlice>()); + DerivedPolicies::delete_item(container, index); + } + + static size_t + base_size(Container& container) + { + return DerivedPolicies::size(container); + } + + static bool + base_contains(Container& container, PyObject* key) + { + extract<Key const&> x(key); + // try if key is an exact Key type + if (x.check()) + { + return DerivedPolicies::contains(container, x()); + } + else + { + // try to convert key to Key type + extract<Key> x(key); + if (x.check()) + return DerivedPolicies::contains(container, x()); + else + return false; + } + } + }; + +}} // namespace boost::python + +#endif // INDEXING_SUITE_JDG20036_HPP diff --git a/boost/python/suite/indexing/map_indexing_suite.hpp b/boost/python/suite/indexing/map_indexing_suite.hpp new file mode 100644 index 0000000000..7fbad4cace --- /dev/null +++ b/boost/python/suite/indexing/map_indexing_suite.hpp @@ -0,0 +1,181 @@ +// (C) Copyright Joel de Guzman 2003. +// 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 MAP_INDEXING_SUITE_JDG20038_HPP +# define MAP_INDEXING_SUITE_JDG20038_HPP + +# include <boost/python/suite/indexing/indexing_suite.hpp> +# include <boost/python/iterator.hpp> +# include <boost/python/call_method.hpp> +# include <boost/python/tuple.hpp> + +namespace boost { namespace python { + + // Forward declaration + template <class Container, bool NoProxy, class DerivedPolicies> + class map_indexing_suite; + + namespace detail + { + template <class Container, bool NoProxy> + class final_map_derived_policies + : public map_indexing_suite<Container, + NoProxy, final_map_derived_policies<Container, NoProxy> > {}; + } + + // The map_indexing_suite class is a predefined indexing_suite derived + // class for wrapping std::map (and std::map like) classes. It provides + // all the policies required by the indexing_suite (see indexing_suite). + // Example usage: + // + // class X {...}; + // + // ... + // + // class_<std::map<std::string, X> >("XMap") + // .def(map_indexing_suite<std::map<std::string, X> >()) + // ; + // + // By default indexed elements are returned by proxy. This can be + // disabled by supplying *true* in the NoProxy template parameter. + // + template < + class Container, + bool NoProxy = false, + class DerivedPolicies + = detail::final_map_derived_policies<Container, NoProxy> > + class map_indexing_suite + : public indexing_suite< + Container + , DerivedPolicies + , NoProxy + , true + , typename Container::value_type::second_type + , typename Container::key_type + , typename Container::key_type + > + { + public: + + typedef typename Container::value_type value_type; + typedef typename Container::value_type::second_type data_type; + typedef typename Container::key_type key_type; + typedef typename Container::key_type index_type; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + + template <class Class> + static void + extension_def(Class& cl) + { + // Wrap the map's element (value_type) + std::string elem_name = "map_indexing_suite_"; + object class_name(cl.attr("__name__")); + extract<std::string> class_name_extractor(class_name); + elem_name += class_name_extractor(); + elem_name += "_entry"; + + typedef typename mpl::if_< + mpl::and_<is_class<data_type>, mpl::bool_<!NoProxy> > + , return_internal_reference<> + , default_call_policies + >::type get_data_return_policy; + + class_<value_type>(elem_name.c_str()) + .def("__repr__", &DerivedPolicies::print_elem) + .def("data", &DerivedPolicies::get_data, get_data_return_policy()) + .def("key", &DerivedPolicies::get_key) + ; + } + + static object + print_elem(typename Container::value_type const& e) + { + return "(%s, %s)" % python::make_tuple(e.first, e.second); + } + + static + typename mpl::if_< + mpl::and_<is_class<data_type>, mpl::bool_<!NoProxy> > + , data_type& + , data_type + >::type + get_data(typename Container::value_type& e) + { + return e.second; + } + + static typename Container::key_type + get_key(typename Container::value_type& e) + { + return e.first; + } + + static data_type& + get_item(Container& container, index_type i_) + { + typename Container::iterator i = container.find(i_); + if (i == container.end()) + { + PyErr_SetString(PyExc_KeyError, "Invalid key"); + throw_error_already_set(); + } + return i->second; + } + + static void + set_item(Container& container, index_type i, data_type const& v) + { + container[i] = v; + } + + static void + delete_item(Container& container, index_type i) + { + container.erase(i); + } + + static size_t + size(Container& container) + { + return container.size(); + } + + static bool + contains(Container& container, key_type const& key) + { + return container.find(key) != container.end(); + } + + static bool + compare_index(Container& container, index_type a, index_type b) + { + return container.key_comp()(a, b); + } + + static index_type + convert_index(Container& /*container*/, PyObject* i_) + { + extract<key_type const&> i(i_); + if (i.check()) + { + return i(); + } + else + { + extract<key_type> i(i_); + if (i.check()) + return i(); + } + + PyErr_SetString(PyExc_TypeError, "Invalid index type"); + throw_error_already_set(); + return index_type(); + } + }; + +}} // namespace boost::python + +#endif // MAP_INDEXING_SUITE_JDG20038_HPP diff --git a/boost/python/suite/indexing/vector_indexing_suite.hpp b/boost/python/suite/indexing/vector_indexing_suite.hpp new file mode 100644 index 0000000000..34c29ecc6f --- /dev/null +++ b/boost/python/suite/indexing/vector_indexing_suite.hpp @@ -0,0 +1,242 @@ +// (C) Copyright Joel de Guzman 2003. +// 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 VECTOR_INDEXING_SUITE_JDG20036_HPP +# define VECTOR_INDEXING_SUITE_JDG20036_HPP + +# include <boost/python/suite/indexing/indexing_suite.hpp> +# include <boost/python/suite/indexing/container_utils.hpp> +# include <boost/python/iterator.hpp> + +namespace boost { namespace python { + + // Forward declaration + template <class Container, bool NoProxy, class DerivedPolicies> + class vector_indexing_suite; + + namespace detail + { + template <class Container, bool NoProxy> + class final_vector_derived_policies + : public vector_indexing_suite<Container, + NoProxy, final_vector_derived_policies<Container, NoProxy> > {}; + } + + // The vector_indexing_suite class is a predefined indexing_suite derived + // class for wrapping std::vector (and std::vector like) classes. It provides + // all the policies required by the indexing_suite (see indexing_suite). + // Example usage: + // + // class X {...}; + // + // ... + // + // class_<std::vector<X> >("XVec") + // .def(vector_indexing_suite<std::vector<X> >()) + // ; + // + // By default indexed elements are returned by proxy. This can be + // disabled by supplying *true* in the NoProxy template parameter. + // + template < + class Container, + bool NoProxy = false, + class DerivedPolicies + = detail::final_vector_derived_policies<Container, NoProxy> > + class vector_indexing_suite + : public indexing_suite<Container, DerivedPolicies, NoProxy> + { + public: + + typedef typename Container::value_type data_type; + typedef typename Container::value_type key_type; + typedef typename Container::size_type index_type; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + + template <class Class> + static void + extension_def(Class& cl) + { + cl + .def("append", &base_append) + .def("extend", &base_extend) + ; + } + + static + typename mpl::if_< + is_class<data_type> + , data_type& + , data_type + >::type + get_item(Container& container, index_type i) + { + return container[i]; + } + + static object + get_slice(Container& container, index_type from, index_type to) + { + if (from > to) + return object(Container()); + return object(Container(container.begin()+from, container.begin()+to)); + } + + static void + set_item(Container& container, index_type i, data_type const& v) + { + container[i] = v; + } + + static void + set_slice(Container& container, index_type from, + index_type to, data_type const& v) + { + if (from > to) { + return; + } + else { + container.erase(container.begin()+from, container.begin()+to); + container.insert(container.begin()+from, v); + } + } + + template <class Iter> + static void + set_slice(Container& container, index_type from, + index_type to, Iter first, Iter last) + { + if (from > to) { + container.insert(container.begin()+from, first, last); + } + else { + container.erase(container.begin()+from, container.begin()+to); + container.insert(container.begin()+from, first, last); + } + } + + static void + delete_item(Container& container, index_type i) + { + container.erase(container.begin()+i); + } + + static void + delete_slice(Container& container, index_type from, index_type to) + { + if (from > to) { + // A null-op. + return; + } + container.erase(container.begin()+from, container.begin()+to); + } + + static size_t + size(Container& container) + { + return container.size(); + } + + static bool + contains(Container& container, key_type const& key) + { + return std::find(container.begin(), container.end(), key) + != container.end(); + } + + static index_type + get_min_index(Container& /*container*/) + { + return 0; + } + + static index_type + get_max_index(Container& container) + { + return container.size(); + } + + static bool + compare_index(Container& /*container*/, index_type a, index_type b) + { + return a < b; + } + + static index_type + convert_index(Container& container, PyObject* i_) + { + extract<long> i(i_); + if (i.check()) + { + long index = i(); + if (index < 0) + index += DerivedPolicies::size(container); + if (index >= long(container.size()) || index < 0) + { + PyErr_SetString(PyExc_IndexError, "Index out of range"); + throw_error_already_set(); + } + return index; + } + + PyErr_SetString(PyExc_TypeError, "Invalid index type"); + throw_error_already_set(); + return index_type(); + } + + static void + append(Container& container, data_type const& v) + { + container.push_back(v); + } + + template <class Iter> + static void + extend(Container& container, Iter first, Iter last) + { + container.insert(container.end(), first, last); + } + + private: + + static void + base_append(Container& container, object v) + { + extract<data_type&> elem(v); + // try if elem is an exact Data + if (elem.check()) + { + DerivedPolicies::append(container, elem()); + } + else + { + // try to convert elem to data_type + extract<data_type> elem(v); + if (elem.check()) + { + DerivedPolicies::append(container, elem()); + } + else + { + PyErr_SetString(PyExc_TypeError, + "Attempting to append an invalid type"); + throw_error_already_set(); + } + } + } + + static void + base_extend(Container& container, object v) + { + std::vector<data_type> temp; + container_utils::extend_container(temp, v); + DerivedPolicies::extend(container, temp.begin(), temp.end()); + } + }; + +}} // namespace boost::python + +#endif // VECTOR_INDEXING_SUITE_JDG20036_HPP |