From 07f397e2edd11e25900a843965272238cba585d6 Mon Sep 17 00:00:00 2001 From: Joel de Guzman Date: Tue, 12 Aug 2003 18:07:11 +0000 Subject: [PATCH] Moved to new "suite" directory [SVN r19561] --- .../python/suite/indexing/container_utils.hpp | 53 +++ .../python/suite/indexing/indexing_suite.hpp | 317 ++++++++++++++++++ .../suite/indexing/vector_indexing_suite.hpp | 175 ++++++++++ 3 files changed, 545 insertions(+) create mode 100644 include/boost/python/suite/indexing/container_utils.hpp create mode 100644 include/boost/python/suite/indexing/indexing_suite.hpp create mode 100644 include/boost/python/suite/indexing/vector_indexing_suite.hpp diff --git a/include/boost/python/suite/indexing/container_utils.hpp b/include/boost/python/suite/indexing/container_utils.hpp new file mode 100644 index 00000000..9758fe78 --- /dev/null +++ b/include/boost/python/suite/indexing/container_utils.hpp @@ -0,0 +1,53 @@ + +// (C) Copyright Joel de Guzman 2003. +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. This +// software is provided "as is" without express or implied warranty, and +// with no claim as to its suitability for any purpose. + +#ifndef PY_CONTAINER_UTILS_JDG20038_HPP +# define PY_CONTAINER_UTILS_JDG20038_HPP + +# include +# include +# include + +namespace boost { namespace python { namespace container_utils { + + template + void + extend_container(Container& container, object l) + { + typedef typename Container::value_type element_t; + + // l must be a list or some container + + for (int i = 0; i < l.attr("__len__")(); i++) + { + object elem(l[i]); + extract x(elem); + // try if elem is an exact element_t type + if (x.check()) + { + container.push_back(x()); + } + else + { + // try to convert elem to element_t type + extract x(elem); + if (x.check()) + { + container.push_back(x()); + } + else + { + PyErr_SetString(PyExc_TypeError, "Incompatible Element Type"); + throw_error_already_set(); + } + } + } + } + +}}} // namespace boost::python::container_utils + +#endif diff --git a/include/boost/python/suite/indexing/indexing_suite.hpp b/include/boost/python/suite/indexing/indexing_suite.hpp new file mode 100644 index 00000000..160b5dd9 --- /dev/null +++ b/include/boost/python/suite/indexing/indexing_suite.hpp @@ -0,0 +1,317 @@ +// (C) Copyright Joel de Guzman 2003. +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. This +// software is provided "as is" without express or implied warranty, and +// with no claim as to its suitability for any purpose. + +#ifndef INDEXING_SUITE_JDG20036_HPP +# define INDEXING_SUITE_JDG20036_HPP + +# include +# include +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { + + // indexing_suite class. This class is the protocol 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 element_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, element_type const& v); + // + // static void + // set_slice( + // Container& container, index_type from, + // index_type to, element_type const& v + // ); + // + // template + // 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 + // 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 Element = typename Container::value_type + , class Key = typename Container::value_type + , class Index = typename Container::size_type + > + class indexing_suite + : public def_arg< + indexing_suite< + Container + , DerivedPolicies + , NoProxy + , NoSlice + , Element + , Key + , Index + > > + { + private: + + typedef mpl::or_< + mpl::bool_ + , mpl::not_ > > + no_proxy; + + typedef detail::container_element + container_element_t; + + typedef typename mpl::if_< + no_proxy + , iterator + , iterator > >::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_ + , detail::slice_helper< + Container + , DerivedPolicies + , proxy_handler + , Element + , Index> + , detail::slice_helper< + Container + , DerivedPolicies + , proxy_handler + , Element + , Index> >::type + slice_handler; + + public: + + template + 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()) + + .def("append", &base_append) + .def("extend", &base_extend) + ; + } + + private: + + static object + base_get_item(back_reference container, PyObject* i) + { + if (PySlice_Check(i)) + return slice_handler::base_get_slice( + container.get(), reinterpret_cast(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, + reinterpret_cast(i), v); + } + else + { + extract elem(v); + // try if elem is an exact Element + if (elem.check()) + { + DerivedPolicies:: + set_item(container, + DerivedPolicies:: + convert_index(container, i), elem()); + } + else + { + // try to convert elem to Element + extract 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, reinterpret_cast(i)); + return; + } + + Index index = DerivedPolicies::convert_index(container, i); + proxy_handler::base_erase_indexes(container, index, index+1); + 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 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 x(key); + if (x.check()) + return DerivedPolicies::contains(container, x()); + else + return false; + } + } + + static void + base_append(Container& container, PyObject* v) + { + extract elem(v); + // try if elem is an exact Element + if (elem.check()) + { + DerivedPolicies::append(container, elem()); + } + else + { + // try to convert elem to Element + extract elem(v); + if (elem.check()) + { + DerivedPolicies::append(container, elem()); + } + else + { + PyErr_SetString(PyExc_TypeError, + "Attempting to append an invalid type"); + throw_error_already_set(); + } + } + } + + static void + base_extend(Container& container, PyObject* v) + { + std::vector temp; + handle<> l_(borrowed(v)); + object l(l_); + container_utils::extend_container(temp, l); + DerivedPolicies::extend(container, temp.begin(), temp.end()); + } + }; + +}} // namespace boost::python + +#endif // INDEXING_SUITE_JDG20036_HPP diff --git a/include/boost/python/suite/indexing/vector_indexing_suite.hpp b/include/boost/python/suite/indexing/vector_indexing_suite.hpp new file mode 100644 index 00000000..18c080d9 --- /dev/null +++ b/include/boost/python/suite/indexing/vector_indexing_suite.hpp @@ -0,0 +1,175 @@ +// (C) Copyright Joel de Guzman 2003. +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. This +// software is provided "as is" without express or implied warranty, and +// with no claim as to its suitability for any purpose. + +#ifndef VECTOR_INDEXING_SUITE_JDG20036_HPP +# define VECTOR_INDEXING_SUITE_JDG20036_HPP + +# include +# include + +namespace boost { namespace python { + + // Forward declaration + template + class vector_indexing_suite; + + namespace detail + { + template + class final + : public vector_indexing_suite > {}; + } + + // The vector_indexing_suite class is a predefined indexing_suite derived + // class for wrapping std::vector (and std::vector like) classes. It provides + // all the policies required by the indexing_suite (see indexing_suite). + // Example usage: + // + // class X {...}; + // + // ... + // + // class_ >("XVec") + // .def(vector_indexing_suite >()) + // ; + // + // By default indexed elements are returned by proxy. This can be + // disabled by supplying *true* in the NoProxy template parameter. + // + template < + class Container, + bool NoProxy = false, + class DerivedPolicies = detail::final > + class vector_indexing_suite + : public indexing_suite + { + public: + + typedef typename Container::value_type element_type; + typedef typename Container::value_type key_type; + typedef typename Container::size_type index_type; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + + static element_type& + get_item(Container& container, index_type i) + { + return container[i]; + } + + static object + get_slice(Container& container, index_type from, index_type to) + { + return object(Container(container.begin()+from, container.begin()+to)); + } + + static void + set_item(Container& container, index_type i, element_type const& v) + { + container[i] = v; + } + + static void + set_slice(Container& container, index_type from, + index_type to, element_type const& v) + { + container.erase(container.begin()+from, container.begin()+to); + container.insert(container.begin()+from, v); + } + + template + static void + set_slice(Container& container, index_type from, + index_type to, Iter first, Iter last) + { + container.erase(container.begin()+from, container.begin()+to); + container.insert(container.begin()+from, first, last); + } + + static void + delete_item(Container& container, index_type i) + { + container.erase(container.begin()+i); + } + + static void + delete_slice(Container& container, index_type from, index_type to) + { + container.erase(container.begin()+from, container.begin()+to); + } + + static size_t + size(Container& container) + { + return container.size(); + } + + static bool + contains(Container& container, key_type const& key) + { + return std::find(container.begin(), container.end(), key) + != container.end(); + } + + static index_type + get_min_index(Container& container) + { + return 0; + } + + static index_type + get_max_index(Container& container) + { + return container.size(); + } + + static index_type + convert_index(Container& container, PyObject* i_) + { + extract i(i_); + if (i.check()) + { + long index = i(); + if (index < 0) + index += DerivedPolicies::size(container); + if (index >= long(container.size()) || index < 0) + { + PyErr_SetString(PyExc_IndexError, "Index out of range"); + throw_error_already_set(); + } + return index; + } + + PyErr_SetString(PyExc_TypeError, "Invalid index type"); + throw_error_already_set(); + return index_type(); + } + + static index_type + adjust_index(index_type current, index_type from, + index_type to, size_type len) + { + return current - (difference_type(to) - from - len); + } + + static void + append(Container& container, element_type const& v) + { + container.push_back(v); + } + + template + static void + extend(Container& container, Iter first, Iter last) + { + container.insert(container.end(), first, last); + } + }; + +}} // namespace boost::python + +#endif // VECTOR_INDEXING_SUITE_JDG20036_HPP