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/python/detail/type_traits.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 detail::is_same<Data, std::string> 126 , detail::is_same<Data, std::complex<float> > 127 , detail::is_same<Data, std::complex<double> > 128 , detail::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> visit(Class & cl) const175 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 extension_def(Class & cl)194 extension_def(Class& cl) 195 { 196 // default. 197 // no more extensions 198 } 199 200 private: 201 202 static object base_get_item(back_reference<Container &> container,PyObject * i)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 base_set_item(Container & container,PyObject * i,PyObject * v)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 base_delete_item(Container & container,PyObject * i)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 base_size(Container & container)267 base_size(Container& container) 268 { 269 return DerivedPolicies::size(container); 270 } 271 272 static bool base_contains(Container & container,PyObject * key)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 296