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