Chris@16: // (C) Copyright Joel de Guzman 2003. Chris@16: // Distributed under the Boost Software License, Version 1.0. (See Chris@16: // accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef INDEXING_SUITE_JDG20036_HPP Chris@16: # define INDEXING_SUITE_JDG20036_HPP Chris@16: Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: Chris@16: namespace boost { namespace python { Chris@16: Chris@16: // indexing_suite class. This class is the facade class for Chris@16: // the management of C++ containers intended to be integrated Chris@16: // to Python. The objective is make a C++ container look and Chris@16: // feel and behave exactly as we'd expect a Python container. Chris@16: // By default indexed elements are returned by proxy. This can be Chris@16: // disabled by supplying *true* in the NoProxy template parameter. Chris@16: // Chris@16: // Derived classes provide the hooks needed by the indexing_suite Chris@16: // to do its job: Chris@16: // Chris@16: // static data_type& Chris@16: // get_item(Container& container, index_type i); Chris@16: // Chris@16: // static object Chris@16: // get_slice(Container& container, index_type from, index_type to); Chris@16: // Chris@16: // static void Chris@16: // set_item(Container& container, index_type i, data_type const& v); Chris@16: // Chris@16: // static void Chris@16: // set_slice( Chris@16: // Container& container, index_type from, Chris@16: // index_type to, data_type const& v Chris@16: // ); Chris@16: // Chris@16: // template Chris@16: // static void Chris@16: // set_slice(Container& container, index_type from, Chris@16: // index_type to, Iter first, Iter last Chris@16: // ); Chris@16: // Chris@16: // static void Chris@16: // delete_item(Container& container, index_type i); Chris@16: // Chris@16: // static void Chris@16: // delete_slice(Container& container, index_type from, index_type to); Chris@16: // Chris@16: // static size_t Chris@16: // size(Container& container); Chris@16: // Chris@16: // template Chris@16: // static bool Chris@16: // contains(Container& container, T const& val); Chris@16: // Chris@16: // static index_type Chris@16: // convert_index(Container& container, PyObject* i); Chris@16: // Chris@16: // static index_type Chris@16: // adjust_index(index_type current, index_type from, Chris@16: // index_type to, size_type len Chris@16: // ); Chris@16: // Chris@16: // Most of these policies are self explanatory. convert_index and Chris@16: // adjust_index, however, deserves some explanation. Chris@16: // Chris@16: // convert_index converts an Python index into a C++ index that the Chris@16: // container can handle. For instance, negative indexes in Python, by Chris@16: // convention, indexes from the right (e.g. C[-1] indexes the rightmost Chris@16: // element in C). convert_index should handle the necessary conversion Chris@16: // for the C++ container (e.g. convert -1 to C.size()-1). convert_index Chris@16: // should also be able to convert the type of the index (A dynamic Python Chris@16: // type) to the actual type that the C++ container expects. Chris@16: // Chris@16: // When a container expands or contracts, held indexes to its elements Chris@16: // must be adjusted to follow the movement of data. For instance, if Chris@16: // we erase 3 elements, starting from index 0 from a 5 element vector, Chris@16: // what used to be at index 4 will now be at index 1: Chris@16: // Chris@16: // [a][b][c][d][e] ---> [d][e] Chris@16: // ^ ^ Chris@16: // 4 1 Chris@16: // Chris@16: // adjust_index takes care of the adjustment. Given a current index, Chris@16: // the function should return the adjusted index when data in the Chris@16: // container at index from..to is replaced by *len* elements. Chris@16: // Chris@16: Chris@16: template < Chris@16: class Container Chris@16: , class DerivedPolicies Chris@16: , bool NoProxy = false Chris@16: , bool NoSlice = false Chris@16: , class Data = typename Container::value_type Chris@16: , class Index = typename Container::size_type Chris@16: , class Key = typename Container::value_type Chris@16: > Chris@16: class indexing_suite Chris@16: : public def_visitor< Chris@16: indexing_suite< Chris@16: Container Chris@16: , DerivedPolicies Chris@16: , NoProxy Chris@16: , NoSlice Chris@16: , Data Chris@16: , Index Chris@16: , Key Chris@16: > > Chris@16: { Chris@16: private: Chris@16: Chris@16: typedef mpl::or_< Chris@16: mpl::bool_ Chris@16: , mpl::not_ > Chris@16: , typename mpl::or_< Chris@16: is_same Chris@16: , is_same > Chris@16: , is_same > Chris@16: , is_same > >::type> Chris@16: no_proxy; Chris@16: Chris@16: typedef detail::container_element Chris@16: container_element_t; Chris@16: Chris@16: #if BOOST_WORKAROUND(BOOST_MSVC, < 1300) Chris@16: struct return_policy : return_internal_reference<> {}; Chris@16: #else Chris@16: typedef return_internal_reference<> return_policy; Chris@16: #endif Chris@16: Chris@16: typedef typename mpl::if_< Chris@16: no_proxy Chris@16: , iterator Chris@16: , iterator >::type Chris@16: def_iterator; Chris@16: Chris@16: typedef typename mpl::if_< Chris@16: no_proxy Chris@16: , detail::no_proxy_helper< Chris@16: Container Chris@16: , DerivedPolicies Chris@16: , container_element_t Chris@16: , Index> Chris@16: , detail::proxy_helper< Chris@16: Container Chris@16: , DerivedPolicies Chris@16: , container_element_t Chris@16: , Index> >::type Chris@16: proxy_handler; Chris@16: Chris@16: typedef typename mpl::if_< Chris@16: mpl::bool_ Chris@16: , detail::no_slice_helper< Chris@16: Container Chris@16: , DerivedPolicies Chris@16: , proxy_handler Chris@16: , Data Chris@16: , Index> Chris@16: , detail::slice_helper< Chris@16: Container Chris@16: , DerivedPolicies Chris@16: , proxy_handler Chris@16: , Data Chris@16: , Index> >::type Chris@16: slice_handler; Chris@16: Chris@16: public: Chris@16: Chris@16: template Chris@16: void visit(Class& cl) const Chris@16: { Chris@16: // Hook into the class_ generic visitation .def function Chris@16: proxy_handler::register_container_element(); Chris@16: Chris@16: cl Chris@16: .def("__len__", base_size) Chris@16: .def("__setitem__", &base_set_item) Chris@16: .def("__delitem__", &base_delete_item) Chris@16: .def("__getitem__", &base_get_item) Chris@16: .def("__contains__", &base_contains) Chris@16: .def("__iter__", def_iterator()) Chris@16: ; Chris@16: Chris@16: DerivedPolicies::extension_def(cl); Chris@16: } Chris@16: Chris@16: template Chris@16: static void Chris@16: extension_def(Class& cl) Chris@16: { Chris@16: // default. Chris@16: // no more extensions Chris@16: } Chris@16: Chris@16: private: Chris@16: Chris@16: static object Chris@16: base_get_item(back_reference container, PyObject* i) Chris@16: { Chris@16: if (PySlice_Check(i)) Chris@16: return slice_handler::base_get_slice( Chris@16: container.get(), static_cast(static_cast(i))); Chris@16: Chris@16: return proxy_handler::base_get_item_(container, i); Chris@16: } Chris@16: Chris@16: static void Chris@16: base_set_item(Container& container, PyObject* i, PyObject* v) Chris@16: { Chris@16: if (PySlice_Check(i)) Chris@16: { Chris@16: slice_handler::base_set_slice(container, Chris@16: static_cast(static_cast(i)), v); Chris@16: } Chris@16: else Chris@16: { Chris@16: extract elem(v); Chris@16: // try if elem is an exact Data Chris@16: if (elem.check()) Chris@16: { Chris@16: DerivedPolicies:: Chris@16: set_item(container, Chris@16: DerivedPolicies:: Chris@16: convert_index(container, i), elem()); Chris@16: } Chris@16: else Chris@16: { Chris@16: // try to convert elem to Data Chris@16: extract elem(v); Chris@16: if (elem.check()) Chris@16: { Chris@16: DerivedPolicies:: Chris@16: set_item(container, Chris@16: DerivedPolicies:: Chris@16: convert_index(container, i), elem()); Chris@16: } Chris@16: else Chris@16: { Chris@16: PyErr_SetString(PyExc_TypeError, "Invalid assignment"); Chris@16: throw_error_already_set(); Chris@16: } Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: static void Chris@16: base_delete_item(Container& container, PyObject* i) Chris@16: { Chris@16: if (PySlice_Check(i)) Chris@16: { Chris@16: slice_handler::base_delete_slice( Chris@16: container, static_cast(static_cast(i))); Chris@16: return; Chris@16: } Chris@16: Chris@16: Index index = DerivedPolicies::convert_index(container, i); Chris@16: proxy_handler::base_erase_index(container, index, mpl::bool_()); Chris@16: DerivedPolicies::delete_item(container, index); Chris@16: } Chris@16: Chris@16: static size_t Chris@16: base_size(Container& container) Chris@16: { Chris@16: return DerivedPolicies::size(container); Chris@16: } Chris@16: Chris@16: static bool Chris@16: base_contains(Container& container, PyObject* key) Chris@16: { Chris@16: extract x(key); Chris@16: // try if key is an exact Key type Chris@16: if (x.check()) Chris@16: { Chris@16: return DerivedPolicies::contains(container, x()); Chris@16: } Chris@16: else Chris@16: { Chris@16: // try to convert key to Key type Chris@16: extract x(key); Chris@16: if (x.check()) Chris@16: return DerivedPolicies::contains(container, x()); Chris@16: else Chris@16: return false; Chris@16: } Chris@16: } Chris@16: }; Chris@16: Chris@16: }} // namespace boost::python Chris@16: Chris@16: #endif // INDEXING_SUITE_JDG20036_HPP