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