source: trunk/environments/g4py/source/boost/detail/indexing_suite_detail.hpp @ 1337

Last change on this file since 1337 was 1337, checked in by garnier, 14 years ago

tag geant4.9.4 beta 1 + modifs locales

File size: 23.0 KB
Line 
1//  (C) Copyright Joel de Guzman 2003.
2//  Distributed under the Boost Software License, Version 1.0. (See
3//  accompanying file LICENSE_1_0.txt or copy at
4//  http://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef INDEXING_SUITE_DETAIL_JDG20036_HPP
7# define INDEXING_SUITE_DETAIL_JDG20036_HPP
8
9# include <boost/python/extract.hpp>
10# include <boost/scoped_ptr.hpp>
11# include <boost/get_pointer.hpp>
12# include <boost/detail/binary_search.hpp>
13# include <boost/numeric/conversion/cast.hpp>
14# include <boost/type_traits/is_pointer.hpp>
15# include <vector>
16# include <map>
17#include <iostream>
18
19namespace boost { namespace python { namespace detail {
20
21#if defined(NDEBUG)
22#define BOOST_PYTHON_INDEXING_CHECK_INVARIANT
23#else
24#define BOOST_PYTHON_INDEXING_CHECK_INVARIANT check_invariant()
25#endif
26   
27    template <class Proxy>
28    struct compare_proxy_index
29    {
30        //  This functor compares a proxy and an index.
31        //  This is used by proxy_group::first_proxy to
32        //  get first proxy with index i.
33               
34        template <class Index>
35        bool operator()(PyObject* prox, Index i) const
36        {
37            typedef typename Proxy::policies_type policies_type;
38            Proxy& proxy = extract<Proxy&>(prox)();
39            return policies_type::
40                compare_index(proxy.get_container(), proxy.get_index(), i);
41        }
42    };       
43 
44    //  The proxy_group class holds a vector of container element
45    //  proxies. First, what is a container element proxy? A container
46    //  element proxy acts like a smart pointer holding a reference to
47    //  a container and an index (see container_element, for details).
48    //
49    //  The proxies are held in a vector always sorted by its index.
50    //  Various functions manage the addition, removal and searching
51    //  of proxies from the vector.
52    //
53    template <class Proxy>
54    class proxy_group
55    {
56    public:
57   
58        typedef typename std::vector<PyObject*>::const_iterator const_iterator;
59        typedef typename std::vector<PyObject*>::iterator iterator;
60        typedef typename Proxy::index_type index_type;
61        typedef typename Proxy::policies_type policies_type;
62       
63        iterator
64        first_proxy(index_type i)
65        {
66            // Return the first proxy with index <= i
67            return boost::detail::lower_bound(
68                proxies.begin(), proxies.end(), 
69                i, compare_proxy_index<Proxy>());
70        }
71
72        void
73        remove(Proxy& proxy)
74        {
75            // Remove a proxy
76            for (iterator iter = first_proxy(proxy.get_index());
77                iter != proxies.end(); ++iter)
78            {
79                if (&extract<Proxy&>(*iter)() == &proxy)
80                {
81                    proxies.erase(iter);
82                    break;
83                }
84            }
85            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
86        }
87
88        void
89        add(PyObject* prox)
90        {
91            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
92            // Add a proxy
93            proxies.insert(
94                first_proxy(extract<Proxy&>(prox)().get_index()), prox);
95            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
96        }
97
98        void
99        erase(index_type i, mpl::false_)
100        {
101            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
102            // Erase the proxy with index i
103            replace(i, i+1, 0);
104            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
105        }
106
107        void
108        erase(index_type i, mpl::true_)
109        {
110            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
111            // Erase the proxy with index i
112           
113            iterator iter = first_proxy(i);
114            extract<Proxy&> p(*iter);
115           
116            if (iter != proxies.end() && p().get_index() == i)
117            {
118                extract<Proxy&> p(*iter);
119                p().detach();
120                proxies.erase(iter);
121            }
122            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
123        }
124
125        void
126        erase(index_type from, index_type to)
127        {
128            // note: this cannot be called when container is not sliceable
129           
130            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
131            // Erase all proxies with indexes from..to
132            replace(from, to, 0);
133            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
134        }
135
136        void
137        replace(
138            index_type from, 
139            index_type to, 
140            typename std::vector<PyObject*>::size_type len)
141        {
142            // note: this cannot be called when container is not sliceable
143
144            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
145            // Erase all proxies with indexes from..to.
146            // Adjust the displaced indexes such that the
147            // final effect is that we have inserted *len*
148            // number of proxies in the vacated region. This
149            // procedure involves adjusting the indexes of
150            // the proxies.
151           
152            iterator left = first_proxy(from);
153            iterator right = proxies.end(); // we'll adjust this later
154           
155            for (iterator iter = left; iter != right; ++iter)
156            {
157                if (extract<Proxy&>(*iter)().get_index() > to)
158                {
159                    right = iter; // adjust right
160                    break;
161                }
162                extract<Proxy&> p(*iter);
163                p().detach();
164            }
165           
166            typename std::vector<PyObject*>::size_type
167                offset = left-proxies.begin();
168            proxies.erase(left, right);
169            right = proxies.begin()+offset;
170
171            while (right != proxies.end())
172            {
173                typedef typename Proxy::container_type::difference_type difference_type;
174                extract<Proxy&> p(*right);
175                p().set_index(
176                    extract<Proxy&>(*right)().get_index() 
177                    - (difference_type(to) - from - len)
178                );
179                   
180                ++right;
181            }
182            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
183        }
184       
185        PyObject*
186        find(index_type i)
187        {
188            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
189            // Find the proxy with *exact* index i.
190            // Return 0 (null) if no proxy with the
191            // given index is found.
192            iterator iter = first_proxy(i);
193            if (iter != proxies.end()
194                && extract<Proxy&>(*iter)().get_index() == i)
195            {
196                BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
197                return *iter;
198            }
199            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
200            return 0;
201        }
202
203        typename std::vector<PyObject*>::size_type
204        size() const
205        {
206            BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
207            // How many proxies are there so far?
208            return proxies.size();
209        } 
210
211    private:
212
213#if !defined(NDEBUG)
214        void
215        check_invariant() const
216        {
217            for (const_iterator i = proxies.begin(); i != proxies.end(); ++i)
218            {
219                if ((*i)->ob_refcnt <= 0)
220                {
221                    PyErr_SetString(PyExc_RuntimeError, 
222                        "Invariant: Proxy vector in an inconsistent state");
223                    throw_error_already_set();
224                }
225               
226                if (i+1 != proxies.end())
227                {
228                    if (extract<Proxy&>(*(i+1))().get_index() ==
229                        extract<Proxy&>(*(i))().get_index())
230                    {
231                        PyErr_SetString(PyExc_RuntimeError, 
232                            "Invariant: Proxy vector in an inconsistent state (duplicate proxy)");
233                        throw_error_already_set();
234                    }
235                }
236            }
237        }
238#endif
239       
240        std::vector<PyObject*> proxies;
241    };
242           
243    // proxy_links holds a map of Container pointers (keys)
244    // with proxy_group(s) (data). Various functions manage
245    // the addition, removal and searching of proxies from
246    // the map.
247    //
248    template <class Proxy, class Container>
249    class proxy_links
250    {
251    public:
252   
253        typedef std::map<Container*, proxy_group<Proxy> > links_t;
254        typedef typename Proxy::index_type index_type;
255
256        void
257        remove(Proxy& proxy)
258        {
259            // Remove a proxy.
260            typename links_t::iterator r = links.find(&proxy.get_container());
261            if (r != links.end())
262            {
263                r->second.remove(proxy);
264                if (r->second.size() == 0)
265                    links.erase(r);
266            }
267        }
268       
269        void
270        add(PyObject* prox, Container& container)
271        {
272            // Add a proxy
273            links[&container].add(prox);
274        }
275       
276        template <class NoSlice>
277        void erase(Container& container, index_type i, NoSlice no_slice)
278        {
279            // Erase the proxy with index i
280            typename links_t::iterator r = links.find(&container);
281            if (r != links.end())
282            {
283                r->second.erase(i, no_slice);
284                if (r->second.size() == 0)
285                    links.erase(r);
286            }
287        }
288       
289        void
290        erase(Container& container, index_type from, index_type to)
291        {
292            // Erase all proxies with indexes from..to
293            typename links_t::iterator r = links.find(&container);
294            if (r != links.end())
295            {
296                r->second.erase(from, to);
297                if (r->second.size() == 0)
298                    links.erase(r);
299            }
300        }
301
302        void
303        replace(
304            Container& container, 
305            index_type from, index_type to, index_type len)
306        {
307            // Erase all proxies with indexes from..to.
308            // Adjust the displaced indexes such that the
309            // final effect is that we have inserted *len*
310            // number of proxies in the vacated region. This
311            // procedure involves adjusting the indexes of
312            // the proxies.
313
314            typename links_t::iterator r = links.find(&container);
315            if (r != links.end())
316            {
317                r->second.replace(from, to, len);
318                if (r->second.size() == 0)
319                    links.erase(r);
320            }
321        }
322       
323        PyObject*
324        find(Container& container, index_type i)
325        {
326            // Find the proxy with *exact* index i.
327            // Return 0 (null) if no proxy with the given
328            // index is found.
329            typename links_t::iterator r = links.find(&container);
330            if (r != links.end())
331                return r->second.find(i);
332            return 0;
333        }
334
335    private:
336   
337        links_t links;
338    };
339   
340    // container_element is our container proxy class.
341    // This class acts like a smart pointer to a container
342    // element. The class holds an index and a reference to
343    // a container. Dereferencing the smart pointer will
344    // retrieve the nth (index) element from the container.
345    //
346    // A container_element can also be detached from the
347    // container. In such a detached state, the container_element
348    // holds a copy of the nth (index) element, which it
349    // returns when dereferenced.
350    //
351    template <class Container, class Index, class Policies>
352    class container_element
353    {
354    public:
355   
356        typedef Index index_type;
357        typedef Container container_type;
358        typedef typename Policies::data_type element_type;
359        typedef Policies policies_type;
360        typedef container_element<Container, Index, Policies> self_t;
361        typedef proxy_group<self_t> links_type;
362       
363        container_element(object container, Index index)
364            : ptr()
365            , container(container)
366            , index(index)
367        {
368        }
369           
370        container_element(container_element const& ce)
371          : ptr(ce.ptr.get() == 0 ? 0 : new element_type(*ce.ptr.get()))
372          , container(ce.container)
373          , index(ce.index)
374        {
375        }
376
377        ~container_element()
378        {
379            if (!is_detached())
380                get_links().remove(*this);
381        }
382                     
383        element_type& operator*() const
384        {
385            if (is_detached())
386                return *get_pointer(ptr);
387            return Policies::get_item(get_container(), index);
388        }
389       
390        element_type* get() const
391        {
392            if (is_detached())
393                return get_pointer(ptr);
394            return &Policies::get_item(get_container(), index);
395        }
396       
397        void
398        detach()
399        {
400            if (!is_detached())
401            {
402                ptr.reset(
403                    new element_type(
404                        Policies::get_item(get_container(), index)));
405                container = object(); // free container. reset it to None
406            }
407        }
408       
409        bool
410        is_detached() const
411        {
412            return get_pointer(ptr) != 0;
413        }
414
415        Container& 
416        get_container() const
417        {
418            return extract<Container&>(container)();
419        }
420       
421        Index
422        get_index() const
423        {
424            return index;
425        }
426
427        void 
428        set_index(Index i)
429        {
430            index = i;
431        }
432 
433        static proxy_links<self_t, Container>&
434        get_links()
435        {
436            // All container_element(s) maintain links to
437            // its container in a global map (see proxy_links).
438            // This global "links" map is a singleton.
439           
440            static proxy_links<self_t, Container> links;
441            return links; // singleton
442        }
443
444    private:
445           
446        container_element& operator=(container_element const& ce);
447
448        scoped_ptr<element_type> ptr;
449        object container;
450        Index index;
451    };
452
453    template <
454          class Container
455        , class DerivedPolicies
456        , class ContainerElement
457        , class Index
458    >
459    struct no_proxy_helper
460    {               
461        static void
462        register_container_element()
463        { 
464        }
465
466        template <class DataType> 
467        static object
468        base_get_item_helper(DataType const& p, mpl::true_)
469        { 
470            return object(ptr(p));
471        }
472
473        template <class DataType> 
474        static object
475        base_get_item_helper(DataType const& x, mpl::false_)
476        { 
477            return object(x);
478        }
479
480        static object
481        base_get_item_(back_reference<Container&> const& container, PyObject* i)
482        { 
483            return base_get_item_helper(
484                DerivedPolicies::get_item(
485                    container.get(), DerivedPolicies::
486                        convert_index(container.get(), i))
487              , is_pointer<BOOST_DEDUCED_TYPENAME Container::value_type>()
488            );
489        }
490
491        static void
492        base_replace_indexes(
493            Container& container, Index from, 
494            Index to, Index n)
495        {
496        }
497
498        template <class NoSlice>
499        static void
500        base_erase_index(
501            Container& container, Index i, NoSlice no_slice)
502        {
503        }
504       
505        static void
506        base_erase_indexes(Container& container, Index from, Index to)
507        {
508        }
509    };           
510         
511    template <
512          class Container
513        , class DerivedPolicies
514        , class ContainerElement
515        , class Index
516    >
517    struct proxy_helper
518    {       
519        static void
520        register_container_element()
521        { 
522            register_ptr_to_python<ContainerElement>();
523        }
524
525        static object
526        base_get_item_(back_reference<Container&> const& container, PyObject* i)
527        { 
528            // Proxy
529            Index idx = DerivedPolicies::convert_index(container.get(), i);
530
531            if (PyObject* shared = 
532                ContainerElement::get_links().find(container.get(), idx))
533            {
534                handle<> h(python::borrowed(shared));
535                return object(h);
536            }
537            else
538            {
539                object prox(ContainerElement(container.source(), idx));
540                ContainerElement::
541                    get_links().add(prox.ptr(), container.get());
542                return prox;
543            }
544        }
545
546        static void
547        base_replace_indexes(
548            Container& container, Index from, 
549            Index to, Index n)
550        {
551            ContainerElement::get_links().replace(container, from, to, n);
552        }
553       
554        template <class NoSlice>
555        static void
556        base_erase_index(
557            Container& container, Index i, NoSlice no_slice)
558        {
559            ContainerElement::get_links().erase(container, i, no_slice);
560        }
561       
562        static void
563        base_erase_indexes(
564            Container& container, Index from, Index to)
565        {
566            ContainerElement::get_links().erase(container, from, to);
567        }
568    };       
569   
570    template <
571          class Container
572        , class DerivedPolicies
573        , class ProxyHandler
574        , class Data
575        , class Index
576    >
577    struct slice_helper
578    {       
579        static object
580        base_get_slice(Container& container, PySliceObject* slice)
581        { 
582            Index from, to;
583            base_get_slice_data(container, slice, from, to);
584            return DerivedPolicies::get_slice(container, from, to);
585        }
586
587        static void
588        base_get_slice_data(
589            Container& container, PySliceObject* slice, Index& from_, Index& to_)
590        {
591            if (Py_None != slice->step) {
592                PyErr_SetString( PyExc_IndexError, "slice step size not supported.");
593                throw_error_already_set();
594            }
595
596            Index min_index = DerivedPolicies::get_min_index(container);
597            Index max_index = DerivedPolicies::get_max_index(container);
598           
599            if (Py_None == slice->start) {
600                from_ = min_index;
601            }
602            else {
603                long from = extract<long>( slice->start);
604                if (from < 0) // Negative slice index
605                    from += max_index;
606                if (from < 0) // Clip lower bounds to zero
607                    from = 0;
608                from_ = boost::numeric_cast<Index>(from);
609                if (from_ > max_index) // Clip upper bounds to max_index.
610                    from_ = max_index;
611            }
612
613            if (Py_None == slice->stop) {
614                to_ = max_index;
615            }
616            else {
617                long to = extract<long>( slice->stop);
618                if (to < 0)
619                    to += max_index;
620                if (to < 0)
621                    to = 0;
622                to_ = boost::numeric_cast<Index>(to);
623                if (to_ > max_index)
624                    to_ = max_index;
625            }
626        }       
627   
628        static void 
629        base_set_slice(Container& container, PySliceObject* slice, PyObject* v)
630        {
631            Index from, to;
632            base_get_slice_data(container, slice, from, to);
633           
634            extract<Data&> elem(v);
635            // try if elem is an exact Data
636            if (elem.check())
637            {
638                ProxyHandler::base_replace_indexes(container, from, to, 1);
639                DerivedPolicies::set_slice(container, from, to, elem());
640            }
641            else
642            {
643                //  try to convert elem to Data
644                extract<Data> elem(v);
645                if (elem.check())
646                {
647                    ProxyHandler::base_replace_indexes(container, from, to, 1);
648                    DerivedPolicies::set_slice(container, from, to, elem());
649                }
650                else
651                {
652                    //  Otherwise, it must be a list or some container
653                    handle<> l_(python::borrowed(v));
654                    object l(l_);
655   
656                    std::vector<Data> temp;
657                    for (int i = 0; i < l.attr("__len__")(); i++)
658                    {
659                        object elem(l[i]);
660                        extract<Data const&> x(elem);
661                        //  try if elem is an exact Data type
662                        if (x.check())
663                        {
664                            temp.push_back(x());
665                        }
666                        else
667                        {
668                            //  try to convert elem to Data type
669                            extract<Data> x(elem);
670                            if (x.check())
671                            {
672                                temp.push_back(x());
673                            }
674                            else
675                            {
676                                PyErr_SetString(PyExc_TypeError, 
677                                    "Invalid sequence element");
678                                throw_error_already_set();
679                            }
680                        }
681                    }         
682                 
683                    ProxyHandler::base_replace_indexes(container, from, to, 
684                        temp.end()-temp.begin());
685                    DerivedPolicies::set_slice(container, from, to, 
686                        temp.begin(), temp.end());
687                }
688            }           
689        }
690       
691        static void 
692        base_delete_slice(Container& container, PySliceObject* slice)
693        { 
694            Index from, to;
695            base_get_slice_data(container, slice, from, to);
696            ProxyHandler::base_erase_indexes(container, from, to);
697            DerivedPolicies::delete_slice(container, from, to);
698        } 
699    };
700   
701    template <
702          class Container
703        , class DerivedPolicies
704        , class ProxyHandler
705        , class Data
706        , class Index
707    >
708    struct no_slice_helper
709    {       
710        static void
711        slicing_not_suported()
712        {
713            PyErr_SetString(PyExc_RuntimeError, "Slicing not supported");
714            throw_error_already_set();
715        }
716       
717        static object
718        base_get_slice(Container& container, PySliceObject* slice)
719        { 
720            slicing_not_suported();
721            return object();
722        }
723   
724        static void 
725        base_set_slice(Container& container, PySliceObject* slice, PyObject* v)
726        {
727            slicing_not_suported();
728        }
729       
730        static void 
731        base_delete_slice(Container& container, PySliceObject* slice)
732        { 
733            slicing_not_suported();
734        } 
735    };
736
737#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
738}} // namespace python::detail
739#endif
740
741    template <class Container, class Index, class Policies>
742    inline typename Policies::data_type* 
743    get_pointer(
744        python::detail::container_element<Container, Index, Policies> const& p)
745    {
746        // Get the pointer of a container_element smart pointer
747        return p.get();
748    }
749
750#ifndef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
751    // Don't hide these other get_pointer overloads
752    using boost::python::get_pointer;
753    using boost::get_pointer;
754}} // namespace python::detail
755#endif
756
757} // namespace boost
758
759#endif // INDEXING_SUITE_DETAIL_JDG20036_HPP
Note: See TracBrowser for help on using the repository browser.