source: trunk/environments/g4py/source/boost/indexing_suite.hpp@ 1349

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

tag geant4.9.4 beta 1 + modifs locales

File size: 9.5 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_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 "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.