indexing_suite.hpp
Go to the documentation of this file.
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 
18 namespace 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  >
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> > >
124 
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
139 
140  typedef typename mpl::if_<
141  no_proxy
143  Container
144  , DerivedPolicies
146  , Index>
148  Container
149  , DerivedPolicies
151  , Index> >::type
153 
154  typedef typename mpl::if_<
155  mpl::bool_<NoSlice>
157  Container
158  , DerivedPolicies
159  , proxy_handler
160  , Data
161  , Index>
163  Container
164  , DerivedPolicies
165  , proxy_handler
166  , Data
167  , Index> >::type
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
static object base_get_item(back_reference< Container & > container, PyObject *i)
static void extension_def(Class &cl)
static size_t base_size(Container &container)
dictionary contains
static void base_set_item(Container &container, PyObject *i, PyObject *v)
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic >::Index size_type
Definition: typedefs.hpp:11
void visit(Class &cl) const
detail::container_element< Container, Index, DerivedPolicies > container_element_t
::xsd::cxx::tree::type container
Definition: Database.h:112
static void base_delete_item(Container &container, PyObject *i)
mpl::if_< no_proxy, iterator< Container >, iterator< Container, return_policy > >::type def_iterator
mpl::if_< no_proxy, detail::no_proxy_helper< Container, DerivedPolicies, container_element_t, Index >, detail::proxy_helper< Container, DerivedPolicies, container_element_t, Index > >::type proxy_handler
static bool base_contains(Container &container, PyObject *key)
mpl::or_< mpl::bool_< NoProxy >, mpl::not_< is_class< Data > > > no_proxy
mpl::if_< mpl::bool_< NoSlice >, detail::no_slice_helper< Container, DerivedPolicies, proxy_handler, Data, Index >, detail::slice_helper< Container, DerivedPolicies, proxy_handler, Data, Index > >::type slice_handler