source: NonGTP/Boost/boost/python/suite/indexing/indexing_suite.hpp @ 857

Revision 857, 9.9 KB checked in by igarcia, 19 years ago (diff)
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_JDG20036_HPP
7# define INDEXING_SUITE_JDG20036_HPP
8
9# include <boost/python/class.hpp>
10# include <boost/python/def_visitor.hpp>
11# include <boost/python/register_ptr_to_python.hpp>
12# include <boost/python/suite/indexing/detail/indexing_suite_detail.hpp>
13# include <boost/python/return_internal_reference.hpp>
14# include <boost/python/iterator.hpp>
15# include <boost/mpl/or.hpp>
16# include <boost/mpl/not.hpp>
17
18namespace boost { namespace python {
19                   
20    // indexing_suite class. This class is the protocol class for
21    // the management of C++ containers intended to be integrated
22    // to Python. The objective is make a C++ container look and
23    // feel and behave exactly as we'd expect a Python container.
24    // By default indexed elements are returned by proxy. This can be
25    // disabled by supplying *true* in the NoProxy template parameter.
26    //
27    // Derived classes provide the hooks needed by the indexing_suite
28    // to do its job:
29    //
30    //      static data_type&
31    //      get_item(Container& container, index_type i);
32    //
33    //      static object
34    //      get_slice(Container& container, index_type from, index_type to);
35    //
36    //      static void
37    //      set_item(Container& container, index_type i, data_type const& v);
38    //
39    //      static void
40    //      set_slice(
41    //         Container& container, index_type from,
42    //         index_type to, data_type const& v
43    //      );
44    //
45    //      template <class Iter>
46    //      static void
47    //      set_slice(Container& container, index_type from,
48    //          index_type to, Iter first, Iter last
49    //      );
50    //
51    //      static void
52    //      delete_item(Container& container, index_type i);
53    //       
54    //      static void
55    //      delete_slice(Container& container, index_type from, index_type to);
56    //       
57    //      static size_t
58    //      size(Container& container);
59    //
60    //      template <class T>
61    //      static bool
62    //      contains(Container& container, T const& val);
63    //       
64    //      static index_type
65    //      convert_index(Container& container, PyObject* i);
66    //       
67    //      static index_type
68    //      adjust_index(index_type current, index_type from,
69    //          index_type to, size_type len
70    //      );
71    //
72    // Most of these policies are self explanatory. convert_index and
73    // adjust_index, however, deserves some explanation.
74    //
75    // convert_index converts an Python index into a C++ index that the
76    // container can handle. For instance, negative indexes in Python, by
77    // convention, indexes from the right (e.g. C[-1] indexes the rightmost
78    // element in C). convert_index should handle the necessary conversion
79    // for the C++ container (e.g. convert -1 to C.size()-1). convert_index
80    // should also be able to convert the type of the index (A dynamic Python
81    // type) to the actual type that the C++ container expects.
82    //
83    // When a container expands or contracts, held indexes to its elements
84    // must be adjusted to follow the movement of data. For instance, if
85    // we erase 3 elements, starting from index 0 from a 5 element vector,
86    // what used to be at index 4 will now be at index 1:
87    //
88    //      [a][b][c][d][e] ---> [d][e]
89    //                   ^           ^
90    //                   4           1
91    //
92    // adjust_index takes care of the adjustment. Given a current index,
93    // the function should return the adjusted index when data in the
94    // container at index from..to is replaced by *len* elements.
95    //
96
97    template <
98          class Container
99        , class DerivedPolicies
100        , bool NoProxy = false
101        , bool NoSlice = false
102        , class Data = typename Container::value_type
103        , class Index = typename Container::size_type
104        , class Key = typename Container::value_type
105    >
106    class indexing_suite
107        : public def_visitor<
108            indexing_suite<
109              Container
110            , DerivedPolicies
111            , NoProxy
112            , NoSlice
113            , Data
114            , Index
115            , Key
116        > >
117    {
118    private:
119       
120        typedef mpl::or_<
121            mpl::bool_<NoProxy>
122          , mpl::not_<is_class<Data> > >
123        no_proxy;
124                   
125        typedef detail::container_element<Container, Index, DerivedPolicies>
126            container_element_t;
127       
128#if BOOST_WORKAROUND(BOOST_MSVC, == 1200)
129        struct return_policy : return_internal_reference<> {};
130#else
131        typedef return_internal_reference<> return_policy;
132#endif
133
134        typedef typename mpl::if_<
135            no_proxy
136          , iterator<Container>
137          , iterator<Container, return_policy> >::type
138        def_iterator;
139       
140        typedef typename mpl::if_<
141            no_proxy
142          , detail::no_proxy_helper<
143                Container
144              , DerivedPolicies
145              , container_element_t
146              , Index>
147          , detail::proxy_helper<
148                Container
149              , DerivedPolicies
150              , container_element_t
151              , Index> >::type
152        proxy_handler;
153
154        typedef typename mpl::if_<
155            mpl::bool_<NoSlice>
156          , detail::no_slice_helper<
157                Container
158              , DerivedPolicies
159              , proxy_handler
160              , Data
161              , Index>
162          , detail::slice_helper<
163                Container
164              , DerivedPolicies
165              , proxy_handler
166              , Data
167              , Index> >::type
168        slice_handler;
169 
170    public:
171     
172        template <class Class>
173        void visit(Class& cl) const
174        {
175            // Hook into the class_ generic visitation .def function
176            proxy_handler::register_container_element();
177           
178            cl
179                .def("__len__", base_size)
180                .def("__setitem__", &base_set_item)
181                .def("__delitem__", &base_delete_item)
182                .def("__getitem__", &base_get_item)
183                .def("__contains__", &base_contains)
184                .def("__iter__", def_iterator())
185            ;
186           
187            DerivedPolicies::extension_def(cl);
188        }       
189       
190        template <class Class>
191        static void
192        extension_def(Class& cl)
193        {
194            // default.
195            // no more extensions
196        }
197
198    private:
199     
200        static object
201        base_get_item(back_reference<Container&> container, PyObject* i)
202        {
203            if (PySlice_Check(i))
204                return slice_handler::base_get_slice(
205                    container.get(), reinterpret_cast<PySliceObject*>(i));
206           
207            return proxy_handler::base_get_item_(container, i);
208        }
209       
210        static void
211        base_set_item(Container& container, PyObject* i, PyObject* v)
212        {
213            if (PySlice_Check(i))
214            {
215                 slice_handler::base_set_slice(container,
216                     reinterpret_cast<PySliceObject*>(i), v);
217            }
218            else
219            {
220                extract<Data&> elem(v);
221                // try if elem is an exact Data
222                if (elem.check())
223                {
224                    DerivedPolicies::
225                        set_item(container,
226                            DerivedPolicies::
227                                convert_index(container, i), elem());
228                }
229                else
230                {
231                    //  try to convert elem to Data
232                    extract<Data> elem(v);
233                    if (elem.check())
234                    {
235                        DerivedPolicies::
236                            set_item(container,
237                                DerivedPolicies::
238                                    convert_index(container, i), elem());
239                    }
240                    else
241                    {
242                        PyErr_SetString(PyExc_TypeError, "Invalid assignment");
243                        throw_error_already_set();
244                    }
245                }
246            }
247        }
248
249        static void
250        base_delete_item(Container& container, PyObject* i)
251        {
252            if (PySlice_Check(i))
253            {
254                slice_handler::base_delete_slice(
255                    container, reinterpret_cast<PySliceObject*>(i));
256                return;
257            }
258           
259            Index index = DerivedPolicies::convert_index(container, i);
260            proxy_handler::base_erase_index(container, index, mpl::bool_<NoSlice>());
261            DerivedPolicies::delete_item(container, index);
262        }
263
264        static size_t
265        base_size(Container& container)
266        {
267            return DerivedPolicies::size(container);
268        }
269
270        static bool
271        base_contains(Container& container, PyObject* key)
272        {
273            extract<Key const&> x(key);
274            //  try if key is an exact Key type
275            if (x.check())
276            {
277                return DerivedPolicies::contains(container, x());
278            }
279            else
280            {
281                //  try to convert key to Key type
282                extract<Key> x(key);
283                if (x.check())
284                    return DerivedPolicies::contains(container, x());
285                else
286                    return false;
287            }           
288        }
289    };
290   
291}} // namespace boost::python
292
293#endif // INDEXING_SUITE_JDG20036_HPP
Note: See TracBrowser for help on using the repository browser.