// (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 # include # include # include # include # include # include # include #include 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 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 bool operator()(PyObject* prox, Index i) const { typedef typename Proxy::policies_type policies_type; Proxy& proxy = extract(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_group { public: typedef typename std::vector::const_iterator const_iterator; typedef typename std::vector::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()); } void remove(Proxy& proxy) { // Remove a proxy for (iterator iter = first_proxy(proxy.get_index()); iter != proxies.end(); ++iter) { if (&extract(*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(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 p(*iter); if (iter != proxies.end() && p().get_index() == i) { extract 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::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(*iter)().get_index() > to) { right = iter; // adjust right break; } extract p(*iter); p().detach(); } typename std::vector::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 p(*right); p().set_index( extract(*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(*iter)().get_index() == i) { BOOST_PYTHON_INDEXING_CHECK_INVARIANT; return *iter; } BOOST_PYTHON_INDEXING_CHECK_INVARIANT; return 0; } typename std::vector::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(*(i+1))().get_index() == extract(*(i))().get_index()) { PyErr_SetString(PyExc_RuntimeError, "Invariant: Proxy vector in an inconsistent state (duplicate proxy)"); throw_error_already_set(); } } } } #endif std::vector 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_links { public: typedef std::map > 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 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_element { public: typedef Index index_type; typedef Container container_type; typedef typename Policies::data_type element_type; typedef Policies policies_type; typedef container_element self_t; typedef proxy_group 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)(); } Index get_index() const { return index; } void set_index(Index i) { index = i; } static proxy_links& 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 links; return links; // singleton } private: container_element& operator=(container_element const& ce); scoped_ptr ptr; object container; Index index; }; template < class Container , class DerivedPolicies , class ContainerElement , class Index > struct no_proxy_helper { static void register_container_element() { } template static object base_get_item_helper(DataType const& p, detail::true_) { return object(ptr(p)); } template static object base_get_item_helper(DataType const& x, detail::false_) { return object(x); } static object base_get_item_(back_reference const& container, PyObject* i) { return base_get_item_helper( DerivedPolicies::get_item( container.get(), DerivedPolicies:: convert_index(container.get(), i)) , is_pointer() ); } static void base_replace_indexes( Container& /*container*/, Index /*from*/, Index /*to*/, Index /*n*/) { } template 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(); } static object base_get_item_(back_reference 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 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( 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(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( slice->stop); if (to < 0) to += max_index; if (to < 0) to = 0; to_ = boost::numeric_cast(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 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 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 temp; for (int i = 0; i < l.attr("__len__")(); i++) { object elem(l[i]); extract 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 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 inline typename Policies::data_type* get_pointer( python::detail::container_element 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