Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-22 10:26:16

0001 //  (C) Copyright Joel de Guzman 2003.
0002 //  Distributed under the Boost Software License, Version 1.0. (See
0003 //  accompanying file LICENSE_1_0.txt or copy at
0004 //  http://www.boost.org/LICENSE_1_0.txt)
0005 
0006 #ifndef INDEXING_SUITE_DETAIL_JDG20036_HPP
0007 # define INDEXING_SUITE_DETAIL_JDG20036_HPP
0008 
0009 # include <boost/python/extract.hpp>
0010 # include <boost/scoped_ptr.hpp>
0011 # include <boost/get_pointer.hpp>
0012 # include <boost/detail/binary_search.hpp>
0013 # include <boost/numeric/conversion/cast.hpp>
0014 # include <boost/python/detail/type_traits.hpp>
0015 # include <vector>
0016 # include <map>
0017 #include <iostream>
0018 
0019 namespace boost { namespace python { namespace detail {
0020 
0021 #if defined(NDEBUG)
0022 #define BOOST_PYTHON_INDEXING_CHECK_INVARIANT
0023 #else
0024 #define BOOST_PYTHON_INDEXING_CHECK_INVARIANT check_invariant()
0025 #endif
0026     
0027     template <class Proxy>
0028     struct compare_proxy_index
0029     {
0030         //  This functor compares a proxy and an index.
0031         //  This is used by proxy_group::first_proxy to
0032         //  get first proxy with index i.
0033                 
0034         template <class Index>
0035         bool operator()(PyObject* prox, Index i) const
0036         {
0037             typedef typename Proxy::policies_type policies_type;
0038             Proxy& proxy = extract<Proxy&>(prox)();
0039             return policies_type::
0040                 compare_index(proxy.get_container(), proxy.get_index(), i);
0041         }
0042     };        
0043  
0044     //  The proxy_group class holds a vector of container element
0045     //  proxies. First, what is a container element proxy? A container 
0046     //  element proxy acts like a smart pointer holding a reference to 
0047     //  a container and an index (see container_element, for details). 
0048     //
0049     //  The proxies are held in a vector always sorted by its index.
0050     //  Various functions manage the addition, removal and searching
0051     //  of proxies from the vector.
0052     //
0053     template <class Proxy>
0054     class proxy_group
0055     {
0056     public:
0057     
0058         typedef typename std::vector<PyObject*>::const_iterator const_iterator;
0059         typedef typename std::vector<PyObject*>::iterator iterator;
0060         typedef typename Proxy::index_type index_type;
0061         typedef typename Proxy::policies_type policies_type;
0062         
0063         iterator
0064         first_proxy(index_type i)
0065         {
0066             // Return the first proxy with index <= i
0067             return boost::detail::lower_bound(
0068                 proxies.begin(), proxies.end(), 
0069                 i, compare_proxy_index<Proxy>());
0070         }
0071 
0072         void
0073         remove(Proxy& proxy)
0074         {
0075             // Remove a proxy
0076             for (iterator iter = first_proxy(proxy.get_index());
0077                 iter != proxies.end(); ++iter)
0078             {
0079                 if (&extract<Proxy&>(*iter)() == &proxy)
0080                 {
0081                     proxies.erase(iter);
0082                     break;
0083                 }
0084             }
0085             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0086         }
0087 
0088         void
0089         add(PyObject* prox)
0090         {
0091             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0092             // Add a proxy
0093             proxies.insert(
0094                 first_proxy(extract<Proxy&>(prox)().get_index()), prox);
0095             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0096         }
0097 
0098         void
0099         erase(index_type i, mpl::false_)
0100         {
0101             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0102             // Erase the proxy with index i 
0103             replace(i, i+1, 0);
0104             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0105         }
0106 
0107         void
0108         erase(index_type i, mpl::true_)
0109         {
0110             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0111             // Erase the proxy with index i 
0112             
0113             iterator iter = first_proxy(i);
0114             extract<Proxy&> p(*iter);
0115             
0116             if (iter != proxies.end() && p().get_index() == i)
0117             {
0118                 extract<Proxy&> p(*iter);
0119                 p().detach();
0120                 proxies.erase(iter);
0121             }
0122             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0123         }
0124 
0125         void
0126         erase(index_type from, index_type to)
0127         {
0128             // note: this cannot be called when container is not sliceable
0129             
0130             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0131             // Erase all proxies with indexes from..to 
0132             replace(from, to, 0);
0133             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0134         }
0135 
0136         void
0137         replace(
0138             index_type from, 
0139             index_type to, 
0140             typename std::vector<PyObject*>::size_type len)
0141         {
0142             // note: this cannot be called when container is not sliceable
0143 
0144             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0145             // Erase all proxies with indexes from..to.
0146             // Adjust the displaced indexes such that the
0147             // final effect is that we have inserted *len*
0148             // number of proxies in the vacated region. This
0149             // procedure involves adjusting the indexes of 
0150             // the proxies.
0151             
0152             iterator left = first_proxy(from);
0153             iterator right = proxies.end(); // we'll adjust this later
0154             
0155             for (iterator iter = left; iter != right; ++iter)
0156             {
0157                 if (extract<Proxy&>(*iter)().get_index() > to)
0158                 {
0159                     right = iter; // adjust right
0160                     break;
0161                 }
0162                 extract<Proxy&> p(*iter);
0163                 p().detach();
0164             }
0165             
0166             typename std::vector<PyObject*>::size_type 
0167                 offset = left-proxies.begin();
0168             proxies.erase(left, right);
0169             right = proxies.begin()+offset;
0170 
0171             while (right != proxies.end())
0172             {
0173                 typedef typename Proxy::container_type::difference_type difference_type;
0174                 extract<Proxy&> p(*right);
0175                 p().set_index(
0176                     extract<Proxy&>(*right)().get_index() 
0177                     - (difference_type(to) - from - len)
0178                 );
0179                     
0180                 ++right;
0181             }
0182             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0183         }
0184         
0185         PyObject*
0186         find(index_type i)
0187         {
0188             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0189             // Find the proxy with *exact* index i.
0190             // Return 0 (null) if no proxy with the 
0191             // given index is found.
0192             iterator iter = first_proxy(i);
0193             if (iter != proxies.end()
0194                 && extract<Proxy&>(*iter)().get_index() == i)
0195             {
0196                 BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0197                 return *iter;
0198             }
0199             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0200             return 0;
0201         }
0202 
0203         typename std::vector<PyObject*>::size_type 
0204         size() const
0205         {
0206             BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
0207             // How many proxies are there so far?
0208             return proxies.size();
0209         } 
0210 
0211     private:
0212 
0213 #if !defined(NDEBUG)
0214         void
0215         check_invariant() const
0216         {
0217             for (const_iterator i = proxies.begin(); i != proxies.end(); ++i)
0218             {
0219                 if ((*i)->ob_refcnt <= 0)
0220                 {
0221                     PyErr_SetString(PyExc_RuntimeError, 
0222                         "Invariant: Proxy vector in an inconsistent state");
0223                     throw_error_already_set();
0224                 }
0225                 
0226                 if (i+1 != proxies.end())
0227                 {
0228                     if (extract<Proxy&>(*(i+1))().get_index() ==
0229                         extract<Proxy&>(*(i))().get_index())
0230                     {
0231                         PyErr_SetString(PyExc_RuntimeError, 
0232                             "Invariant: Proxy vector in an inconsistent state (duplicate proxy)");
0233                         throw_error_already_set();
0234                     }
0235                 }
0236             }
0237         }
0238 #endif
0239         
0240         std::vector<PyObject*> proxies;
0241     };
0242             
0243     // proxy_links holds a map of Container pointers (keys)
0244     // with proxy_group(s) (data). Various functions manage 
0245     // the addition, removal and searching of proxies from 
0246     // the map.
0247     //
0248     template <class Proxy, class Container>
0249     class proxy_links
0250     {
0251     public:
0252     
0253         typedef std::map<Container*, proxy_group<Proxy> > links_t;
0254         typedef typename Proxy::index_type index_type;
0255 
0256         void
0257         remove(Proxy& proxy)
0258         {
0259             // Remove a proxy.
0260             typename links_t::iterator r = links.find(&proxy.get_container());
0261             if (r != links.end())
0262             {
0263                 r->second.remove(proxy);
0264                 if (r->second.size() == 0)
0265                     links.erase(r);
0266             }
0267         }
0268         
0269         void
0270         add(PyObject* prox, Container& container)
0271         {
0272             // Add a proxy
0273             links[&container].add(prox);
0274         }
0275         
0276         template <class NoSlice>
0277         void erase(Container& container, index_type i, NoSlice no_slice)
0278         {
0279             // Erase the proxy with index i 
0280             typename links_t::iterator r = links.find(&container);
0281             if (r != links.end())
0282             {
0283                 r->second.erase(i, no_slice);
0284                 if (r->second.size() == 0)
0285                     links.erase(r);
0286             }
0287         }
0288         
0289         void
0290         erase(Container& container, index_type from, index_type to)
0291         {
0292             // Erase all proxies with indexes from..to 
0293             typename links_t::iterator r = links.find(&container);
0294             if (r != links.end())
0295             {
0296                 r->second.erase(from, to);
0297                 if (r->second.size() == 0)
0298                     links.erase(r);
0299             }
0300         }
0301 
0302         void
0303         replace(
0304             Container& container, 
0305             index_type from, index_type to, index_type len)
0306         {
0307             // Erase all proxies with indexes from..to.
0308             // Adjust the displaced indexes such that the
0309             // final effect is that we have inserted *len*
0310             // number of proxies in the vacated region. This
0311             // procedure involves adjusting the indexes of 
0312             // the proxies.
0313 
0314             typename links_t::iterator r = links.find(&container);
0315             if (r != links.end())
0316             {
0317                 r->second.replace(from, to, len);
0318                 if (r->second.size() == 0)
0319                     links.erase(r);
0320             }
0321         }
0322         
0323         PyObject*
0324         find(Container& container, index_type i)
0325         {
0326             // Find the proxy with *exact* index i.
0327             // Return 0 (null) if no proxy with the given 
0328             // index is found.
0329             typename links_t::iterator r = links.find(&container);
0330             if (r != links.end())
0331                 return r->second.find(i);
0332             return 0;
0333         }
0334 
0335     private:
0336     
0337         links_t links;
0338     };
0339     
0340     // container_element is our container proxy class.
0341     // This class acts like a smart pointer to a container
0342     // element. The class holds an index and a reference to
0343     // a container. Dereferencing the smart pointer will
0344     // retrieve the nth (index) element from the container.
0345     //
0346     // A container_element can also be detached from the
0347     // container. In such a detached state, the container_element
0348     // holds a copy of the nth (index) element, which it 
0349     // returns when dereferenced.
0350     //
0351     template <class Container, class Index, class Policies>
0352     class container_element
0353     {
0354     public:
0355     
0356         typedef Index index_type;
0357         typedef Container container_type;
0358         typedef typename Policies::data_type element_type;
0359         typedef Policies policies_type;
0360         typedef container_element<Container, Index, Policies> self_t;
0361         typedef proxy_group<self_t> links_type;
0362         
0363         container_element(object container, Index index)
0364             : ptr()
0365             , container(container)
0366             , index(index)
0367         {
0368         }
0369             
0370         container_element(container_element const& ce)
0371           : ptr(ce.ptr.get() == 0 ? 0 : new element_type(*ce.ptr.get()))
0372           , container(ce.container)
0373           , index(ce.index)
0374         {
0375         }
0376 
0377         ~container_element()
0378         {
0379             if (!is_detached())
0380                 get_links().remove(*this);
0381         }
0382                       
0383         element_type& operator*() const
0384         {
0385             if (is_detached())
0386                 return *get_pointer(ptr);
0387             return Policies::get_item(get_container(), index);
0388         }
0389         
0390         element_type* get() const
0391         {
0392             if (is_detached())
0393                 return get_pointer(ptr);
0394             return &Policies::get_item(get_container(), index);
0395         }
0396         
0397         void
0398         detach()
0399         {
0400             if (!is_detached())
0401             {
0402                 ptr.reset(
0403                     new element_type(
0404                         Policies::get_item(get_container(), index)));
0405                 container = object(); // free container. reset it to None
0406             }
0407         }
0408         
0409         bool
0410         is_detached() const
0411         {
0412             return get_pointer(ptr) != 0;
0413         }
0414 
0415         Container& 
0416         get_container() const
0417         {
0418             return extract<Container&>(container)();
0419         }
0420         
0421         Index 
0422         get_index() const
0423         {
0424             return index;
0425         }
0426 
0427         void 
0428         set_index(Index i)
0429         {
0430             index = i;
0431         }
0432  
0433         static proxy_links<self_t, Container>&
0434         get_links()
0435         {
0436             // All container_element(s) maintain links to
0437             // its container in a global map (see proxy_links).
0438             // This global "links" map is a singleton.
0439             
0440             static proxy_links<self_t, Container> links;
0441             return links; // singleton
0442         }
0443 
0444     private:
0445             
0446         container_element& operator=(container_element const& ce);
0447 
0448         scoped_ptr<element_type> ptr;
0449         object container;
0450         Index index;
0451     };
0452 
0453     template <
0454           class Container
0455         , class DerivedPolicies
0456         , class ContainerElement
0457         , class Index
0458     >
0459     struct no_proxy_helper
0460     {                
0461         static void
0462         register_container_element()
0463         { 
0464         }
0465 
0466         template <class DataType> 
0467         static object
0468         base_get_item_helper(DataType const& p, detail::true_)
0469         { 
0470             return object(ptr(p));
0471         }
0472 
0473         template <class DataType> 
0474         static object
0475         base_get_item_helper(DataType const& x, detail::false_)
0476         { 
0477             return object(x);
0478         }
0479 
0480         static object
0481         base_get_item_(back_reference<Container&> const& container, PyObject* i)
0482         { 
0483             return base_get_item_helper(
0484                 DerivedPolicies::get_item(
0485                     container.get(), DerivedPolicies::
0486                         convert_index(container.get(), i))
0487               , is_pointer<BOOST_DEDUCED_TYPENAME Container::value_type>()
0488             );
0489         }
0490 
0491         static void
0492         base_replace_indexes(
0493             Container& /*container*/, Index /*from*/, 
0494             Index /*to*/, Index /*n*/)
0495         {
0496         }
0497 
0498         template <class NoSlice>
0499         static void
0500         base_erase_index(
0501             Container& /*container*/, Index /*i*/, NoSlice /*no_slice*/)
0502         {
0503         }
0504         
0505         static void
0506         base_erase_indexes(Container& /*container*/, Index /*from*/, Index /*to*/)
0507         {
0508         }
0509     };            
0510           
0511     template <
0512           class Container
0513         , class DerivedPolicies
0514         , class ContainerElement
0515         , class Index
0516     >
0517     struct proxy_helper
0518     {        
0519         static void
0520         register_container_element()
0521         { 
0522             register_ptr_to_python<ContainerElement>();
0523         }
0524 
0525         static object
0526         base_get_item_(back_reference<Container&> const& container, PyObject* i)
0527         { 
0528             // Proxy
0529             Index idx = DerivedPolicies::convert_index(container.get(), i);
0530 
0531             if (PyObject* shared = 
0532                 ContainerElement::get_links().find(container.get(), idx))
0533             {
0534                 handle<> h(python::borrowed(shared));
0535                 return object(h);
0536             }
0537             else
0538             {
0539                 object prox(ContainerElement(container.source(), idx));
0540                 ContainerElement::
0541                     get_links().add(prox.ptr(), container.get());
0542                 return prox;
0543             }
0544         }
0545 
0546         static void
0547         base_replace_indexes(
0548             Container& container, Index from, 
0549             Index to, Index n)
0550         {
0551             ContainerElement::get_links().replace(container, from, to, n);
0552         }
0553         
0554         template <class NoSlice>
0555         static void
0556         base_erase_index(
0557             Container& container, Index i, NoSlice no_slice)
0558         {
0559             ContainerElement::get_links().erase(container, i, no_slice);
0560         }
0561         
0562         static void
0563         base_erase_indexes(
0564             Container& container, Index from, Index to)
0565         {
0566             ContainerElement::get_links().erase(container, from, to);
0567         }
0568     };        
0569     
0570     template <
0571           class Container
0572         , class DerivedPolicies
0573         , class ProxyHandler
0574         , class Data
0575         , class Index
0576     >
0577     struct slice_helper
0578     {        
0579         static object 
0580         base_get_slice(Container& container, PySliceObject* slice)
0581         { 
0582             Index from, to;
0583             base_get_slice_data(container, slice, from, to);
0584             return DerivedPolicies::get_slice(container, from, to);
0585         }
0586 
0587         static void
0588         base_get_slice_data(
0589             Container& container, PySliceObject* slice, Index& from_, Index& to_)
0590         {
0591             if (Py_None != slice->step) {
0592                 PyErr_SetString( PyExc_IndexError, "slice step size not supported.");
0593                 throw_error_already_set();
0594             }
0595 
0596             Index min_index = DerivedPolicies::get_min_index(container);
0597             Index max_index = DerivedPolicies::get_max_index(container);
0598             
0599             if (Py_None == slice->start) {
0600                 from_ = min_index;
0601             }
0602             else {
0603                 long from = extract<long>( slice->start);
0604                 if (from < 0) // Negative slice index
0605                     from += max_index;
0606                 if (from < 0) // Clip lower bounds to zero
0607                     from = 0;
0608                 from_ = boost::numeric_cast<Index>(from);
0609                 if (from_ > max_index) // Clip upper bounds to max_index.
0610                     from_ = max_index;
0611             }
0612 
0613             if (Py_None == slice->stop) {
0614                 to_ = max_index;
0615             }
0616             else {
0617                 long to = extract<long>( slice->stop);
0618                 if (to < 0)
0619                     to += max_index;
0620                 if (to < 0)
0621                     to = 0;
0622                 to_ = boost::numeric_cast<Index>(to);
0623                 if (to_ > max_index)
0624                     to_ = max_index;
0625             }
0626         }        
0627    
0628         static void 
0629         base_set_slice(Container& container, PySliceObject* slice, PyObject* v)
0630         {
0631             Index from, to;
0632             base_get_slice_data(container, slice, from, to);
0633             
0634             extract<Data&> elem(v);
0635             // try if elem is an exact Data
0636             if (elem.check())
0637             {
0638                 ProxyHandler::base_replace_indexes(container, from, to, 1);
0639                 DerivedPolicies::set_slice(container, from, to, elem());
0640             }
0641             else
0642             {
0643                 //  try to convert elem to Data
0644                 extract<Data> elem(v);
0645                 if (elem.check())
0646                 {
0647                     ProxyHandler::base_replace_indexes(container, from, to, 1);
0648                     DerivedPolicies::set_slice(container, from, to, elem());
0649                 }
0650                 else
0651                 {
0652                     //  Otherwise, it must be a list or some container
0653                     handle<> l_(python::borrowed(v));
0654                     object l(l_);
0655     
0656                     std::vector<Data> temp;
0657                     for (int i = 0; i < l.attr("__len__")(); i++)
0658                     {
0659                         object elem(l[i]);
0660                         extract<Data const&> x(elem);
0661                         //  try if elem is an exact Data type
0662                         if (x.check())
0663                         {
0664                             temp.push_back(x());
0665                         }
0666                         else
0667                         {
0668                             //  try to convert elem to Data type
0669                             extract<Data> x(elem);
0670                             if (x.check())
0671                             {
0672                                 temp.push_back(x());
0673                             }
0674                             else
0675                             {
0676                                 PyErr_SetString(PyExc_TypeError, 
0677                                     "Invalid sequence element");
0678                                 throw_error_already_set();
0679                             }
0680                         }
0681                     }          
0682                   
0683                     ProxyHandler::base_replace_indexes(container, from, to, 
0684                         temp.end()-temp.begin());
0685                     DerivedPolicies::set_slice(container, from, to, 
0686                         temp.begin(), temp.end());
0687                 }
0688             }            
0689         }
0690         
0691         static void 
0692         base_delete_slice(Container& container, PySliceObject* slice)
0693         { 
0694             Index from, to;
0695             base_get_slice_data(container, slice, from, to);
0696             ProxyHandler::base_erase_indexes(container, from, to);
0697             DerivedPolicies::delete_slice(container, from, to);
0698         }  
0699     };
0700     
0701     template <
0702           class Container
0703         , class DerivedPolicies
0704         , class ProxyHandler
0705         , class Data
0706         , class Index
0707     >
0708     struct no_slice_helper
0709     {        
0710         static void
0711         slicing_not_suported()
0712         {
0713             PyErr_SetString(PyExc_RuntimeError, "Slicing not supported");
0714             throw_error_already_set();
0715         }
0716         
0717         static object 
0718         base_get_slice(Container& /*container*/, PySliceObject* /*slice*/)
0719         { 
0720             slicing_not_suported();
0721             return object();
0722         }
0723    
0724         static void 
0725         base_set_slice(Container& /*container*/, PySliceObject* /*slice*/, PyObject* /*v*/)
0726         {
0727             slicing_not_suported();
0728         }
0729         
0730         static void 
0731         base_delete_slice(Container& /*container*/, PySliceObject* /*slice*/)
0732         { 
0733             slicing_not_suported();
0734         }  
0735     };
0736 
0737 #ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
0738 }} // namespace python::detail
0739 #endif
0740 
0741     template <class Container, class Index, class Policies>
0742     inline typename Policies::data_type* 
0743     get_pointer(
0744         python::detail::container_element<Container, Index, Policies> const& p)
0745     {
0746         // Get the pointer of a container_element smart pointer
0747         return p.get();
0748     }
0749 
0750 #ifndef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
0751     // Don't hide these other get_pointer overloads
0752     using boost::python::get_pointer;
0753     using boost::get_pointer;
0754 }} // namespace python::detail
0755 #endif
0756 
0757 } // namespace boost
0758 
0759 #endif // INDEXING_SUITE_DETAIL_JDG20036_HPP