From b7ae3540e23e6c5ce8c3351eedc4c696733567d3 Mon Sep 17 00:00:00 2001 From: Raoul Gough Date: Wed, 3 Sep 2003 20:47:11 +0000 Subject: [PATCH] Initial revision [SVN r1471] --- include/boost/python/suite/indexing/CONTENTS | 21 + .../python/suite/indexing/container_proxy.hpp | 576 ++++++++++++++++++ .../python/suite/indexing/container_suite.hpp | 349 +++++++++++ .../python/suite/indexing/containers.txt | 129 ++++ .../boost/python/suite/indexing/indexing.cpp | 222 +++++++ .../python/suite/indexing/indexing_suite.cpp | 165 +++++ .../python/suite/indexing/iterator_suite.hpp | 143 +++++ .../boost/python/suite/indexing/sample.out | 44 ++ .../suite/indexing/shared_proxy_impl.hpp | 103 ++++ .../python/suite/indexing/suite_utils.hpp | 211 +++++++ .../python/suite/indexing/testcontprox.cpp | 145 +++++ .../python/suite/indexing/testindexing.py | 70 +++ .../boost/python/suite/indexing/testlist.py | 158 +++++ .../suite/indexing/vector_indexing_suite.py | 344 +++++++++++ .../indexing/vector_indexing_suite_ext.cpp | 61 ++ .../boost/python/suite/indexing/vis.hpp.patch | 33 + 16 files changed, 2774 insertions(+) create mode 100755 include/boost/python/suite/indexing/CONTENTS create mode 100755 include/boost/python/suite/indexing/container_proxy.hpp create mode 100755 include/boost/python/suite/indexing/container_suite.hpp create mode 100755 include/boost/python/suite/indexing/containers.txt create mode 100755 include/boost/python/suite/indexing/indexing.cpp create mode 100755 include/boost/python/suite/indexing/indexing_suite.cpp create mode 100755 include/boost/python/suite/indexing/iterator_suite.hpp create mode 100644 include/boost/python/suite/indexing/sample.out create mode 100755 include/boost/python/suite/indexing/shared_proxy_impl.hpp create mode 100755 include/boost/python/suite/indexing/suite_utils.hpp create mode 100755 include/boost/python/suite/indexing/testcontprox.cpp create mode 100755 include/boost/python/suite/indexing/testindexing.py create mode 100755 include/boost/python/suite/indexing/testlist.py create mode 100755 include/boost/python/suite/indexing/vector_indexing_suite.py create mode 100755 include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp create mode 100644 include/boost/python/suite/indexing/vis.hpp.patch diff --git a/include/boost/python/suite/indexing/CONTENTS b/include/boost/python/suite/indexing/CONTENTS new file mode 100755 index 00000000..58cf66bb --- /dev/null +++ b/include/boost/python/suite/indexing/CONTENTS @@ -0,0 +1,21 @@ + +container_proxy.hpp Container wrapper with semi-transparent proxies +shared_proxy_impl.hpp Component shared by proxies that refer to same element +testcontprox.cpp Simple tests for container_proxy +testindexing.py Test various instantiations of the indexing_suite +indexing.cpp Python extensions required for testindexing.py +vis.hpp.patch Simple vector_indexing_suite.hpp patch for indexing.cpp +sample.out Sample output of testindexing.py +vector_indexing_suite_ext.cpp Modified extension module from python/test +vector_indexing_suite.py Python test script from python/test (unmodified) + + +older stuff +=========== + +container_suite.hpp Container traits +iterator_suite.hpp Traits for iterator ranges +suite_utils.hpp Traits utilities +indexing_suite.cpp Traits tests +containers.txt Analysis of Python sequence vs. C++ containers +testlist.py Test behaviour of real Python lists diff --git a/include/boost/python/suite/indexing/container_proxy.hpp b/include/boost/python/suite/indexing/container_proxy.hpp new file mode 100755 index 00000000..26b39e30 --- /dev/null +++ b/include/boost/python/suite/indexing/container_proxy.hpp @@ -0,0 +1,576 @@ +// -*- mode:c++; switch-modules-target: "testcontprox.cpp" -*- +// +// Header file container_proxy.hpp +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this material for any purpose is hereby +// granted without fee, provided the above notices are retained on all +// copies. Permission to modify the material and to distribute modified +// versions is granted, provided the above notices are retained, and a +// notice that the material was modified is included with the above +// copyright notice. +// +// History +// ======= +// 2003/ 8/26 rmg File creation +// +// $Id$ +// + +#ifndef container_proxy_rmg_20030826_included +#define container_proxy_rmg_20030826_included + +#include "shared_proxy_impl.hpp" +#include +#include +#include +#include +#include + +template struct identity { + static T & get(T & obj) { return obj; } + static T const & get(T const & obj) { return obj; } +}; + +template struct deref { + template static T & get (U & ptr) { return *ptr; } + template static T const & get (U const & ptr) { return *ptr; } +}; + +template > +class container_proxy +{ + typedef container_proxy self_type; + typedef typename Container::iterator raw_iterator; + typedef std::iterator_traits raw_iterator_traits; + +public: + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + + typedef shared_proxy_impl shared_proxy; + + typedef typename Container::value_type raw_value_type; + + struct const_element_proxy; + + class element_proxy + { + friend class const_element_proxy; + + typedef shared_proxy proxy_type; + typedef boost::shared_ptr proxy_pointer; + + proxy_pointer mPtr; + + public: + typedef typename proxy_type::value_type value_type; + typedef typename proxy_type::reference reference; + typedef typename proxy_type::pointer pointer; + typedef typename proxy_type::iterator_category iterator_category; + typedef typename proxy_type::difference_type difference_type; + + typedef value_type element_type; // Alias for register_ptr_to_python + + element_proxy () : mPtr () { } + explicit element_proxy (proxy_type *ptr) : mPtr (ptr) { } + element_proxy (proxy_pointer const &ptr) : mPtr (ptr) { } + + explicit element_proxy (raw_value_type const &val) + : mPtr (new proxy_type(val)) + { + // Create new standalone value (i.e. detached) + } + + reference operator* () const { return mPtr->operator*(); } + pointer operator-> () const { return (*mPtr).operator->(); } + pointer get () const { return operator->(); } // Alias for pointer_holder + + // Implicit conversion to raw_value_type + operator reference () const { return operator*(); } + + // These are necessary (at least) while the indexing suite insists + // on converting the real container's value_type to the proxy + // container's value_type when going from Python to C++. If the + // suite would just pass the real container's value_type through, + // our implicit conversion to value_type might suffice. + bool operator== (value_type const &other) { return (**this) == other; } + bool operator!= (value_type const &other) { return (**this) != other; } + bool operator< (value_type const &other) { return (**this) < other; } + bool operator> (value_type const &other) { return (**this) > other; } + + element_proxy &operator= (value_type const ©) + { + proxy_type &proxy (*mPtr); + container_proxy *container = proxy.owner(); + size_type index = proxy.index(); + + if (container) + { + container->replace (index, copy); + // Proxy was attached before, but is now detached. Make sure + // we now refer to the new element, instead of the detached + // copy of the old element + mPtr = container->at (index).mPtr; + + // Note: in the special case that this we and the container + // proxy itself have the only references to the + // shared_proxy_impl, it is not necessary to first detach + // the proxy. Maybe best to implement when changing to + // intrusive_ptr instead of shared_ptr. + } + + else + { + *proxy = copy; + } + + return *this; + } + + element_proxy &operator= (element_proxy const ©) + { + // This is the most dubious bit of the fudge. The indexing_suite's + // implementation of __setitem__ tries to pass us our value_type, + // which is actually of type element_proxy + return (*this) = *copy; + } + + size_t use_count() const { return mPtr.use_count(); } // For debugging + }; + + struct const_element_proxy + { + typedef shared_proxy proxy_type; + typedef boost::shared_ptr proxy_pointer; + + proxy_pointer mPtr; + + public: + typedef typename proxy_type::value_type const value_type; + typedef value_type &reference; + typedef value_type *pointer; + typedef typename proxy_type::iterator_category iterator_category; + typedef typename proxy_type::difference_type difference_type; + + const_element_proxy () : mPtr () { } + explicit const_element_proxy (proxy_type *ptr) : mPtr (ptr) { } + const_element_proxy (proxy_pointer const &ptr) : mPtr (ptr) { } + const_element_proxy (element_proxy const ©) : mPtr (copy.mPtr) { } + + explicit const_element_proxy (raw_value_type const &val) + : mPtr (new proxy_type(val)) + { + // Create new standalone value (i.e. detached) + } + + reference operator* () const { return mPtr->operator*(); } + pointer operator-> () const { return mPtr->operator->(); } + + // Implicit conversion to raw_value_type + operator reference () const { return operator*(); } + + size_t use_count() const { return mPtr.use_count(); } // For debugging + }; + + typedef element_proxy value_type; + typedef element_proxy reference; // Already has reference semantics + typedef const_element_proxy const_value_type; + +public: + struct iterator + { + typedef typename raw_iterator_traits::difference_type difference_type; + typedef std::random_access_iterator_tag iterator_category; + typedef element_proxy value_type; + typedef element_proxy *pointer; + typedef element_proxy reference; // Already has reference semantics + + iterator (container_proxy *p, size_type i) : ptr (p), index (i) { } + + iterator (container_proxy *p, raw_iterator iter) + : ptr (p), index (iter - p->container().begin()) + { + } + + reference operator*() const { return ptr->at(index); } + pointer operator->() const { return &ptr->at(index); } + reference operator[](size_type s) { return ptr->at (index + s); } + + iterator &operator++ () { ++index; return *this; } + iterator &operator++ (int) { ++index; return *this; } + iterator &operator+= (size_type s) { index += s; return *this; } + + iterator &operator-- () { --index; return *this; } + iterator &operator-- (int) { --index; return *this; } + iterator &operator-= (size_type s) { index -= s; return *this; } + + iterator operator+ (size_type s) const { return iterator(*this) += s; } + iterator operator- (size_type s) const { return iterator(*this) -= s; } + difference_type operator- (iterator i) const { return index - i.index; } + + bool operator== (iterator const &other) const { + return (ptr == other.ptr) && (index == other.index); + } + + bool operator!= (iterator const &other) const { return !(*this == other); } + + // public: + // Extensions to the normal iterator interface + // void replace (value_type const ©) { ptr->replace (index, copy); } + + public: + friend class container_proxy; + container_proxy *ptr; + size_type index; + }; + +public: + // Constructors + container_proxy (); + explicit container_proxy (HeldType const &h); + template container_proxy (Iter, Iter); + + Container & container(); // Should be private? + Container const &container() const; // Should be private? + + element_proxy at (size_type index); + const_element_proxy at (size_type index) const; + + element_proxy operator[] (size_type index) { return at(index); } + const_element_proxy operator[] (size_type index) const { return at(index); } + + size_type size() const { return container().size(); } + size_type capacity() const { return container().capacity(); } + void reserve(size_type s) { container().reserve(s); } + +public: + iterator begin() { return iterator (this, 0); } + iterator end() { return iterator (this, container().size()); } + + iterator erase (iterator); + iterator erase (iterator, iterator); + iterator insert (iterator, raw_value_type const &); + template void insert (iterator, Iter, Iter); + + void push_back (raw_value_type const ©) { insert (end(), copy); } + + element_proxy pop_back () { + element_proxy result = at (end() - 1); + erase (end() - 1); + return result; + } + +public: + // Proxies for a given index must be detached before overwriting + // that container element. + void detach_proxy (size_type index); + void detach_proxies (size_type from, size_type to); + void detach_proxies (iterator from, iterator to); + +public: + // Convenient replacement of elements (automatic proxy detachment) + void replace (size_type index, raw_value_type const &); + template void replace (size_type index, Iter, Iter); + +private: + // Overloads for insertions with/without useful std::distance + template + void insert (iterator, Iter, Iter, std::forward_iterator_tag); + + template + void insert (iterator, Iter, Iter, std::input_iterator_tag); + +private: + typedef boost::shared_ptr pointer_impl; + typedef std::map MapType; + typedef typename MapType::iterator MapIterator; + typedef typename MapType::value_type MapEntry; + +private: + static void detach_if_shared (MapEntry const &); + void adjustIndexes (MapIterator, MapIterator, long); + +private: + HeldType myHeldType; + MapType myMap; +}; + +template +container_proxy +::container_proxy () + : myHeldType () + , myMap () +{ +} + +template +container_proxy +::container_proxy (HeldType const &heldType) + : myHeldType (heldType) + , myMap () +{ +} + +template +template +container_proxy +::container_proxy (Iter start, Iter finish) + : myHeldType () + , myMap () +{ + insert (begin(), start, finish); +} + +template +Container & +container_proxy +::container () +{ + return Accessor::get (myHeldType); +} + +template +Container const & +container_proxy +::container () const +{ + return Accessor::get (myHeldType); +} + +template +typename container_proxy::element_proxy +container_proxy +::at (size_type index) +{ + pointer_impl &entry = myMap[index]; + + if (!entry.get()) + { + entry.reset (new shared_proxy (this, index)); + } + + return element_proxy (entry); +} + +template +typename container_proxy::const_element_proxy +container_proxy +::at (size_type index) const +{ + // const_cast allows insertion into map. Maybe this is wrong, and + // there can be no const-version of at. Alternatively, maybe + // the map member variable should be declared mutable. + + container_proxy *mutable_this = const_cast(this); + + return const_element_proxy (mutable_this->at (index)); +} + +template +void +container_proxy +::replace (size_type index, raw_value_type const ©) +{ + detach_proxy (index); + container().at(index) = copy; +} + +template +template +void +container_proxy +::replace (size_type index, Iter from, Iter to) +{ + while (from != to) + { + replace (index++, *from++); + } +} + +template +typename container_proxy::iterator +container_proxy +::erase (iterator iter) +{ + return erase (iter, iter + 1); +} + +template +typename container_proxy::iterator +container_proxy +::erase (iterator from, iterator to) +{ + assert (from.ptr == this); + assert (to.ptr == this); + + size_type deleting = to.index - from.index; + MapIterator erase_begin = myMap.lower_bound (from.index); + MapIterator erase_end = myMap.lower_bound (to.index); + + // Detach any proxies for the soon-to-be-erased elements + std::for_each (erase_begin, erase_end, detach_if_shared); + myMap.erase (erase_begin, erase_end); // Note: erase_end remains valid + + // Adjust the indexes of any following proxies + adjustIndexes (erase_end, myMap.end(), -deleting); + + // Erase the elements from the real container + raw_iterator result = container().erase (container().begin() + from.index + , container().begin() + to.index); + + return iterator (this, result); +} + +template +typename container_proxy::iterator +container_proxy +::insert (iterator iter, raw_value_type const ©) +{ + assert (iter.ptr == this); + + // Adjust indexes from iter.index, since insert will go before iter + adjustIndexes (myMap.lower_bound (iter.index), myMap.end(), 1); + + // Insert the element into the real container + raw_iterator result = container().insert (container().begin() + iter.index + , copy); + + return iterator (this, result); +} + +template +template +void +container_proxy +::insert (iterator iter, Iter from, Iter to) +{ + // Forward insertion to the right overloaded version + typedef typename std::iterator_traits::iterator_category category; + insert (iter, from, to, category()); +} + +template +template +void +container_proxy +::insert (iterator iter, Iter from, Iter to, std::forward_iterator_tag) +{ + // insert overload for iterators where we can get distance() + + assert (iter.ptr == this); + + // Adjust indexes from iter.index, since insert will go before iter + adjustIndexes (myMap.lower_bound (iter.index) + , myMap.end() + , std::distance (from, to)); + + // Insert the element into the real container + container().insert (container().begin() + iter.index, from, to); +} + +template +template +void +container_proxy +::insert (iterator iter, Iter from, Iter to, std::input_iterator_tag) +{ + // insert overload for iterators where we *can't* get distance() + // so just insert elements one at a time + while (from != to) + { + iter = insert (iter, *from++) + 1; + } +} + +template +void +container_proxy +::detach_if_shared (MapEntry const &ent) +{ + if (!ent.second.unique()) + { + ent.second->detach(); + } + // If the pointer isn't shared, don't bother causing a copy of the + // container element, since the proxy is about to be deleted. +} + +template +void +container_proxy +::detach_proxy (size_type index) +{ + MapIterator iter = myMap.find (index); + + if (iter != myMap.end()) + { + detach_if_shared (*iter); + myMap.erase (iter); + } +} + +template +void +container_proxy +::detach_proxies (size_type fromIndex, size_type toIndex) +{ + MapIterator from = myMap.lower_bound (fromIndex); + MapIterator to = myMap.lower_bound (toIndex); + std::for_each (from, to, detach_if_shared); + myMap.erase (from, to); +} + +template +void +container_proxy +::detach_proxies (iterator from, iterator to) +{ + assert (from.ptr == this); + assert (to.ptr == this); + detach_proxies (from.index, to.index); +} + +template +void +container_proxy +::adjustIndexes (MapIterator from, MapIterator to, long offset) +{ + // Adjust indexes in the given range of proxies by the given offset. + // The adjustment is done by erasing and re-inserting the entries + // in the map. + // + // Could provide a hint iterator to the map insertion calls, except + // in the case that "from" is right at the start of the container + // (the hint must be the element *before* the one to be inserted, + // and there is no element before the first one). This would mean + // additional complexity to deal with the special case somehow. + + while (from != to) + { + MapIterator next (from); + ++next; // Find next element before invalidating the current one + + pointer_impl ptr (from->second); // Copy the shared pointer + myMap.erase (from); // Remove the map copy of it + + if (!ptr.unique()) + { + // Reinsert only if there are other pointers "out there" + // referring to the shared proxy + + ptr->myIndex += offset; + myMap.insert (typename MapType::value_type (ptr->myIndex, ptr)); + } + + from = next; + } +} + +#endif // container_proxy_rmg_20030826_included diff --git a/include/boost/python/suite/indexing/container_suite.hpp b/include/boost/python/suite/indexing/container_suite.hpp new file mode 100755 index 00000000..3112e947 --- /dev/null +++ b/include/boost/python/suite/indexing/container_suite.hpp @@ -0,0 +1,349 @@ +// -*- mode:c++ -*- +// +// Header file container_suite.hpp +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this material for any purpose is hereby +// granted without fee, provided the above notices are retained on all +// copies. Permission to modify the material and to distribute modified +// versions is granted, provided the above notices are retained, and a +// notice that the material was modified is included with the above +// copyright notice. +// +// History +// ======= +// 2003/ 8/23 rmg File creation +// +// $Id$ +// + +#ifndef container_suite_rmg_20030823_included +#define container_suite_rmg_20030823_included + +#include "suite_utils.hpp" +#include "iterator_suite.hpp" +#include + +#include +#include +#include +#include +#include + +namespace indexing { + ////////////////////////////////////////////////////////////////////////// + // Iterator pair container emulation + ////////////////////////////////////////////////////////////////////////// + + template + class iterator_pair + { + private: + typedef typename boost::call_traits::param_type iterator_param; + typedef std::iterator_traits std_traits; + + public: + typedef typename std_traits::reference reference; + typedef Iterator iterator; + typedef typename std_traits::difference_type size_type; + typedef typename std_traits::difference_type difference_type; + typedef typename std_traits::value_type value_type; + typedef typename std_traits::pointer pointer; + + // Can't provide: const_iterator, allocator_type, reverse_iterator + // or const_reverse_iterator. Could probably provide (but don't) + // const_reference and const_pointer. These would be the same + // as reference and pointer if Iterator is itself a const_iterator. + + public: + iterator_pair (iterator_param, iterator_param); + + iterator begin() const; + iterator end() const; + + public: + // Only sensible for random_access iterators + size_type size () const; + reference operator[] (size_type) const; + + private: + iterator myBegin; + iterator myEnd; + }; + + template + iterator_pair::iterator_pair (iterator_param begin + , iterator_param end) + : myBegin (begin) + , myEnd (end) + { + } + + template + typename iterator_pair::iterator + iterator_pair::begin() const + { + return myBegin; + } + + template + typename iterator_pair::iterator + iterator_pair::end() const + { + return myEnd; + } + + template + typename iterator_pair::size_type + iterator_pair::size() const + { + return std::distance (begin(), end()); + } + + template + typename iterator_pair::reference + iterator_pair::operator[](size_type index) const + { + return *(begin() + index); + } + + ///////////////////////////////////////////////////////////////////////// + // Container traits + ///////////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////////// + // Traits for the iterator_pair container emulator + ///////////////////////////////////////////////////////////////////////// + + template + struct iterator_pair_traits + : public + iterator_detail::traits_by_category::type + { + typedef IteratorPair container; + typedef typename IteratorPair::size_type size_type; + typedef typename IteratorPair::size_type index_type; // at() + + static bool const has_insert = false; + static bool const has_erase = false; + static bool const has_pop_back = false; + static bool const has_push_back = false; + + // Default implementations of support functions + typedef container_algorithms algorithms; + }; + + ///////////////////////////////////////////////////////////////////////// + // Lowest common denominator (almost all "real" containers would + // meet at least these requirements) + ///////////////////////////////////////////////////////////////////////// + + template + struct default_container_traits + { + protected: + static bool const is_mutable = ! boost::is_const::value; + + public: + typedef Container container; + + typedef typename Container::value_type value_type; + typedef typename Container::reference reference; + typedef typename Container::difference_type difference_type; + typedef typename Container::size_type size_type; + typedef typename Container::size_type index_type; // at() + typedef value_type key_type; // find, count, ... + + // Should probably select iterator or const_iterator on the + // basis of is_mutable + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + static bool const has_copyable_iter = true; + + static bool const has_random_access + = ::indexing::iterator_traits::has_random_access; + + static bool const has_mutable_ref + = is_mutable && is_mutable_ref::value; + + // has_mutable_ref basically means that the container supports + // in-place replacement of values (e.g. the associative containers + // *don't*) + + static bool const is_reversible = has_mutable_ref; + + static bool const has_insert = is_mutable; + static bool const has_erase = is_mutable; + static bool const has_pop_back = false; + static bool const has_push_back = false; + + // Default implementations of support functions + typedef container_algorithms algorithms; + }; + + ///////////////////////////////////////////////////////////////////////// + // Sequences (list, deque, vector) + ///////////////////////////////////////////////////////////////////////// + + template + struct default_sequence_traits : public default_container_traits + { + static bool const has_pop_back = is_mutable; + static bool const has_push_back = is_mutable; + }; + + template + struct list_traits : public default_sequence_traits + { + // Some special algo's for list (using member functions) + typedef list_algorithms algorithms; + }; + + ///////////////////////////////////////////////////////////////////////// + // Associative containers set and multiset + ///////////////////////////////////////////////////////////////////////// + + template + struct set_traits : public default_container_traits + { + // Special algo's for set types (using member functions) + typedef assoc_algorithms algorithms; + }; + + ///////////////////////////////////////////////////////////////////////// + // Associative containers map and multimap + ///////////////////////////////////////////////////////////////////////// + + template + struct map_traits : public default_container_traits + { + typedef typename Container::key_type key_type; // find, count, ... + + // Special algo's for map types (using member functions) + typedef assoc_algorithms algorithms; + }; + + ///////////////////////////////////////////////////////////////////////// + // Automated trait selection + ///////////////////////////////////////////////////////////////////////// + + namespace container_details { + template struct traits_by_type; + + // traits_by_type instances should include two typedefs, one for + // the non-const version of the container, and one for the + // const version. This saves having to have two specializations + // of traits_by_type for every kind of container. + + // std::set + template + class traits_by_type > + { + typedef std::set Container; + + public: + typedef set_traits mutable_type; + typedef set_traits const_type; + }; + + // std::multiset + template + class traits_by_type > + { + typedef std::multiset Container; + + public: + typedef set_traits mutable_type; + typedef set_traits const_type; + }; + + // std::map + template + class traits_by_type > + { + typedef std::map Container; + + public: + typedef map_traits mutable_type; + typedef map_traits const_type; + }; + + // std::multimap + template + class traits_by_type > + { + typedef std::multimap Container; + + public: + typedef map_traits mutable_type; + typedef map_traits const_type; + }; + + // std::vector + template + class traits_by_type > + { + typedef std::vector Container; + + public: + typedef default_sequence_traits mutable_type; + typedef default_sequence_traits const_type; + }; + + // std::deque + template + class traits_by_type > + { + typedef std::deque Container; + + public: + typedef default_sequence_traits mutable_type; + typedef default_sequence_traits const_type; + }; + + // std::list + template + class traits_by_type > + { + typedef std::list Container; + + public: + typedef list_traits mutable_type; + typedef list_traits const_type; + }; + + // Iterator ranges + template + class traits_by_type > + { + typedef ::indexing::iterator_pair Container; + + public: + typedef iterator_pair_traits mutable_type; + typedef iterator_pair_traits const_type; // ? + }; + } + + // Select the right traits for each supported kind of container + + // Generic version (mutable containers) + template + struct container_traits + : public container_details::traits_by_type::mutable_type + { + }; + + // Partial specialization for const containers + template + struct container_traits + : public container_details::traits_by_type::const_type + { + }; +} + +#endif // container_suite_rmg_20030823_included diff --git a/include/boost/python/suite/indexing/containers.txt b/include/boost/python/suite/indexing/containers.txt new file mode 100755 index 00000000..6e0a99a0 --- /dev/null +++ b/include/boost/python/suite/indexing/containers.txt @@ -0,0 +1,129 @@ + +Copyright (c) 2003 Raoul M. Gough + +This material is provided "as is", with absolutely no warranty expressed +or implied. Any use is at your own risk. + +Permission to use or copy this material for any purpose is hereby +granted without fee, provided the above notices are retained on all +copies. Permission to modify the material and to distribute modified +versions for any purpose is granted, provided the above notices are +retained, and a notice that the material was modified is included with +the above copyright notice. + +$Id$ + +============================================================================== + +This file contains information and comments about mapping Python +sequence functionality onto STL containers or iterator pairs. Primary +sources of information are the Python Library Reference, sections +2.2.6 (Sequence types) and 2.2.6.4 (Mutable sequence types), the +Python Reference Manual (aka Language Reference) sections 3.3 (Special +method names), 3.3.4 (Emulating container types) and 3.3.6 (Eumlating +numeric types) and the C++ standard (ISO/IEC 14882:1998(E)). + +============================================================================== + +Sequence functionality - Python expressions, the Python method used to +implement the operation (in order of preference) and possible C++ +equivalents. + + Python feature Implementation (preferential order) Possible C++ emulation + ============== =================================== ====================== + + x in s __contains__, __iter__, __getitem__ find algo/member fn + x not in s __contains__, __iter__, __getitem__ find algo/member fn + s + t __add__ or __radd__ constr., insert [1] + s * n , n * s __mul__ or __rmul__ constr., insert [1] + s[i] __getitem__ [] or *(iter + i) + s[i:j] __getslice__ [3], __getitem__ constr(iter,iter)[2] + len(s) __len__ distance/size memfn + iter(s) __iter__ constr. iter pair + min(s) __iter__, __getitem__ (?) + max(s) __iter__, __getitem__ (?) + +[1] have to construct new container (make vector from iterator pairs?) +[2] construct copy of container contents or return iterator pair? +[3] __getslice__, __setslice__ and __delslice__ are deprecated + + +Mutable sequence functionality: + + Python feature Implementation (preferential order) Possible C++ emulation + ============== =================================== ====================== + + s[i] = x __setitem__ s[i]=x or *(iter+i)=x + s[i:j] = t __setslice__ [3], __setitem__ operator=,insert,erase + del s[i:j] __delslice__ [3], __delitem__ erase + del s[i] __delitem__ erase + s += t __iadd__ insert(iter,iter) + s *= t __imul__ insert(iter,iter) + s.append(x) append insert(obj) + s.extend(x) extend [ python list type only ] insert(iter,iter) + s.count(x) count count algo/mem fn + s.index(x) index find algo/mem fn + s.insert(i, x) insert insert + s.pop([i]) pop pop_back/erase? + s.remove(x) remove remove_if and erase? + s.reverse() reverse reverse + s.sort([cmpfunc]) sort sort algo? + +[3] __getslice__, __setslice__ and __delslice__ are deprecated + +============================================================================== + +Cross-reference. C++ functions for the various types of iterator-pair +ranges and the python methods that they can implement. + + C++ functions Minimum iterator type Python function(s) + ============= ===================== ================== + + ++, * input_iterator[4] next + copyable iterator forward_iterator[5] __iter__ + std::distance forward_iterator[5] __len__ + std::find forward_iterator[5] __contains__, index + std::count forward_iterator[5] count + std::reverse bidirectional_iter[6] reverse + +, * random_access_iter[4] __getitem__ [7] + +, non-const * random_access_iter[6] __setitem__(index)[8] + std::sort random_access_iter[6] sort([cmpfunc]) + +[4] iterator_traits::reference can be reference to const +[5] input_iterator is unsuitable because copies aren't independant +[6] iterator_traits::reference must not be reference to const +[7] maybe return an iterator pair if step is 1 or -1 (reverse_iterator) +[8] support slices where replacement sequence is of equal length? + +Continued cross-reference. C++ functions for the various types of STL +containers and the python methods that they can implement. + + C++ functions Minimum container type Python function(s) + ============= ====================== ================== + + insert any insert + member function size any(?) __len__ + nonconst [] any except list __setitem__(index/key) + pop_back non-associative pop() + push_back non-associative append, __iadd__, __imul__ + std::find, erase non-associative remove(value) + member function find associative __contains__ + find, erase associative pop(key) + erase associative remove, __delitem__(key) + nonconst [], insert, erase vector, deque __setitem__(slice) + erase vector, deque pop(index), __delitem__ + insert multimap, multiset __iadd__, __imul__ + member function sort list sort + member function reverse list reverse + +============================================================================= + +random notes +============ + +What about __lt__, __le__, __ne__, __eq__, __ge__ and __gt__ ? + +What about debugging? Maybe the traits classes need to identify +themselves, so that traits-clients could trace information about what +traits were used. Otherwise, if expected features aren't available, +how would the programmer figure out why not? diff --git a/include/boost/python/suite/indexing/indexing.cpp b/include/boost/python/suite/indexing/indexing.cpp new file mode 100755 index 00000000..aba20e38 --- /dev/null +++ b/include/boost/python/suite/indexing/indexing.cpp @@ -0,0 +1,222 @@ +// -*- mode:c++; switch-modules-target: "testindexing.py" -*- +// +// Module indexing.cpp +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any non-commercial purpose +// is hereby granted without fee, provided the above notices are retained +// on all copies. Permission to modify the code and to distribute +// modified code for non-commercial purposes is granted, provided the +// above notices are retained, and a notice that the code was modified is +// included with the above copyright notice. +// +// History +// ======= +// 2003/ 8/18 rmg File creation +// +// $Id$ +// + +#include "container_proxy.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + bool gIntWrapperTrace = true; +} + +struct IntWrapper { + static unsigned ourObjectCounter; + int mObjNumber; + int mI; + + IntWrapper () + : mObjNumber (ourObjectCounter++) + , mI (0) + { + if (gIntWrapperTrace) + { + printf ("IntWrapper %u ()\n", mObjNumber); + } + } + + explicit IntWrapper (int i) + : mObjNumber (ourObjectCounter++) + , mI (i) + { + if (gIntWrapperTrace) + { + printf ("IntWrapper %u (%d)\n" + , mObjNumber + , mI); + } + } + + IntWrapper (IntWrapper const &other) + : mObjNumber (ourObjectCounter++) + , mI (other.mI) + { + if (gIntWrapperTrace) + { + printf ("IntWrapper %u (IntWrapper %u)\n" + , mObjNumber + , other.mObjNumber); + } + } + + IntWrapper &operator= (IntWrapper const &other) + { + if (gIntWrapperTrace) + { + printf ("IntWrapper %u = IntWrapper %u\n" + , mObjNumber + , other.mObjNumber); + } + + mI = other.mI; + + return *this; + } + + ~IntWrapper () + { + if (gIntWrapperTrace) + { + printf ("~IntWrapper %u\n", mObjNumber); + } + } + + void increment() + { + if (gIntWrapperTrace) + { + printf ("IntWrapper %u::increment\n", mObjNumber); + } + + ++mI; + } + + operator boost::shared_ptr () const + { + if (gIntWrapperTrace) + { + printf ("IntWrapper %u shared_ptr conversion\n", mObjNumber); + } + + return boost::shared_ptr (new IntWrapper (*this)); + } +}; + +unsigned IntWrapper::ourObjectCounter = 0; + +bool operator== (IntWrapper const &lhs, IntWrapper const &rhs) +{ + return lhs.mI == rhs.mI; +} + +bool operator< (IntWrapper const &lhs, IntWrapper const &rhs) +{ + return lhs.mI < rhs.mI; +} + +std::ostream &operator<< (std::ostream &strm, IntWrapper const &iw) +{ + strm << iw.mI; + return strm; +} + +template +std::string proxy_repr (typename ContainerProxy::value_type const &proxy) +{ + return boost::lexical_cast (*proxy); +} + +template +void proxy_increment (typename ContainerProxy::value_type const &proxy) +{ + (*proxy).increment(); +} + +std::string pointer_repr (boost::shared_ptr const &ptr) +{ + return boost::lexical_cast (*ptr); +} + +void pointer_increment (boost::shared_ptr const &ptr) +{ + (*ptr).increment(); +} + +void setTrace (int onoff) +{ + gIntWrapperTrace = onoff; +} + +IntWrapper *get_pointer (container_proxy >::value_type const &proxy) +{ + return &(*proxy); +} + +BOOST_PYTHON_MODULE (indexing) +{ + boost::python::def ("trace", &setTrace); + + typedef std::vector Container; + typedef container_proxy ProxyContainer; + typedef boost::shared_ptr Pointer; + typedef std::vector PointerContainer; + + using boost::python::vector_indexing_suite; + + // typedef vector_indexing_suite Suite; + typedef vector_indexing_suite ProxySuite; + typedef vector_indexing_suite ProxyContainerSuite; + typedef vector_indexing_suite PointerContainerSuite; + + boost::python::class_ ("Vector") + .def (ProxySuite()) + .def ("reserve", &Container::reserve); + + boost::python::class_ ("ProxyVector") + .def (ProxyContainerSuite()) + .def ("reserve", &ProxyContainer::reserve); + + boost::python::class_ ("PointerVector") + .def (PointerContainerSuite()) + .def ("reserve", &PointerContainer::reserve); + + boost::python::implicitly_convertible (); + + boost::python::class_ ("IntWrapper", boost::python::init()) + .def ("increment", &IntWrapper::increment) + .def ("__repr__", &boost::lexical_cast) + ; + + // Ultimately, all of the value_type's properties should be provided + // via something like register_ptr_to_python with double-dereferencing. + // boost::python::class_ ("Container__value_type", boost::python::init()) + // .def ("increment", proxy_increment) + // .def ("__repr__", proxy_repr) + // ; + boost::python::register_ptr_to_python(); + + boost::python::implicitly_convertible (); + + boost::python::class_ ("Pointer", boost::python::no_init) + .def ("increment", pointer_increment) + .def ("__repr__", pointer_repr) + ; +} diff --git a/include/boost/python/suite/indexing/indexing_suite.cpp b/include/boost/python/suite/indexing/indexing_suite.cpp new file mode 100755 index 00000000..1d7033e0 --- /dev/null +++ b/include/boost/python/suite/indexing/indexing_suite.cpp @@ -0,0 +1,165 @@ +// -*- mode:c++ -*- +// +// Module indexing_suite.cc +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this material for any purpose is hereby +// granted without fee, provided the above notices are retained on all +// copies. Permission to modify the material and to distribute modified +// versions is granted, provided the above notices are retained, and a +// notice that the material was modified is included with the above +// copyright notice. +// +// History +// ======= +// 2003/ 8/22 rmg File creation +// +// $Id$ +// + +#include "container_suite.hpp" +#include "iterator_suite.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#include + static int DEMANGLE_STATUS = 0; +# define TYPENAME(t) \ + abi::__cxa_demangle(typeid(t).name(), 0, 0, &DEMANGLE_STATUS) +#else +# define TYPENAME(t) \ + typeid(t).name() +#endif // __GXX__ + +template +void test () +{ + std::cout + << TYPENAME(Traits) + << "\n" + + << Traits::has_copyable_iter + << " iterator_traits::has_copyable_iter\n" + + << Traits::is_reversible + << " iterator_traits::is_reversible\n" + + << Traits::has_random_access + << " iterator_traits::has_random_access\n" + + << Traits::has_mutable_ref + << " iterator_traits::has_mutable_ref\n" + + << Traits::has_insert + << " iterator_traits::has_insert\n" + + << Traits::has_erase + << " iterator_traits::has_erase\n" + + << Traits::has_pop_back + << " iterator_traits::has_pop_back\n" + + << Traits::has_push_back + << " iterator_traits::has_push_back\n" + + << "=====================================\n" + ; +} + +int main () +{ + using indexing::iterator_traits; + using indexing::container_traits; + using indexing::iterator_pair; + + test > > >(); + test::const_iterator> > >(); + test::iterator> > >(); + test::const_iterator> > >(); + test::iterator> > >(); + test::const_iterator> > >(); + test::iterator> > >(); + test::const_iterator> > >(); + test::iterator> > >(); + test > >(); + test > >(); + + test > >(); + test const> >(); + test > >(); + test const> >(); + test > >(); + test const> >(); + test > >(); + test const> >(); + + typedef iterator_pair Range; + int array[16] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Range range (array, array + 16); + + container_traits::algorithms::reverse (range); + + std::cout + << *container_traits::algorithms::at (range, 0) << " " + << *container_traits::algorithms::at (range, 1) << " " + << *container_traits::algorithms::at (range, 2) << " " + << "\n"; + + container_traits::algorithms::sort (range); + + std::cout + << *container_traits::algorithms::at (range, 3) << " " + << *container_traits::algorithms::at (range, 4) << " " + << *container_traits::algorithms::at (range, 5) << " " + << "\n"; + + std::cout + << container_traits::algorithms::size (range) + << "\n" + << container_traits::algorithms::find (range, 8) - array + << "\n" + << container_traits::algorithms::count (range, 0) + << "\n" + ; + + typedef std::list List; + List l (array, array + 16); + + container_traits::algorithms::reverse (l); + std::copy (l.begin(), l.end(), std::ostream_iterator(std::cout, " ")); + std::cout << "\n"; + + container_traits::algorithms::sort (l); + std::copy (l.begin(), l.end(), std::ostream_iterator(std::cout, " ")); + std::cout << "\n"; + + typedef std::set Set; + Set s (l.begin(), l.end()); + std::cout + << container_traits::algorithms::count (s, 0) + << "\n" + << *container_traits::algorithms::find (s, 8) + << "\n" + ; + + typedef std::multiset Multiset; + Multiset multi_s (l.begin(), l.end()); + std::cout + << container_traits::algorithms::count (multi_s, 0) + << "\n" + << *container_traits::algorithms::find (multi_s, 8) + << "\n" + ; +} diff --git a/include/boost/python/suite/indexing/iterator_suite.hpp b/include/boost/python/suite/indexing/iterator_suite.hpp new file mode 100755 index 00000000..146e3669 --- /dev/null +++ b/include/boost/python/suite/indexing/iterator_suite.hpp @@ -0,0 +1,143 @@ +// -*- mode:c++ -*- +// +// Header file iterator_suite.hpp +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this material for any purpose is hereby +// granted without fee, provided the above notices are retained on all +// copies. Permission to modify the material and to distribute modified +// versions is granted, provided the above notices are retained, and a +// notice that the material was modified is included with the above +// copyright notice. +// +// History +// ======= +// 2003/ 8/23 rmg File creation +// +// $Id$ +// + +#ifndef iterator_suite_rmg_20030823_included +#define iterator_suite_rmg_20030823_included + +#include "suite_utils.hpp" + +#include +#include +#include + +namespace indexing { + ////////////////////////////////////////////////////////////////////////// + // Indexing traits for containers based on iterator pairs + ////////////////////////////////////////////////////////////////////////// + + template + struct input_iterator_traits + { + private: + typedef std::iterator_traits std_traits; + + public: + typedef Iterator iterator; + typedef typename std_traits::value_type value_type; + typedef typename std_traits::reference reference; + typedef typename std_traits::difference_type difference_type; + typedef typename std_traits::difference_type index_type; + typedef value_type key_type; // find, count, ... + + static bool const has_copyable_iter = false; + static bool const is_reversible = false; + static bool const has_random_access = false; + static bool const has_mutable_ref = is_mutable_ref::value; + }; + + template + struct forward_iterator_traits + : public input_iterator_traits + { + static bool const has_copyable_iter = true; + }; + + template + struct bidirectional_iterator_traits + : public forward_iterator_traits + { + static bool const is_reversible = has_mutable_ref; + }; + + template + struct random_access_iterator_traits + : public bidirectional_iterator_traits + { + static bool const has_random_access = true; + }; + + namespace iterator_detail { + typedef char input_iter_sizer[1]; + typedef char forward_iter_sizer[2]; + typedef char bidirectional_iter_sizer[3]; + typedef char random_access_iter_sizer[4]; + + input_iter_sizer &sizer (std::input_iterator_tag const &); + forward_iter_sizer &sizer (std::forward_iterator_tag const &); + bidirectional_iter_sizer &sizer (std::bidirectional_iterator_tag const &); + random_access_iter_sizer &sizer (std::random_access_iterator_tag const &); + + template struct traits_by_size { }; + + template<> + struct traits_by_size { + template + struct traits { + typedef input_iterator_traits type; + }; + }; + + template<> + struct traits_by_size { + template + struct traits { + typedef forward_iterator_traits type; + }; + }; + + template<> + struct traits_by_size { + template + struct traits { + typedef bidirectional_iterator_traits type; + }; + }; + + template<> + struct traits_by_size { + template + struct traits { + typedef random_access_iterator_traits type; + }; + }; + + template + class traits_by_category { + typedef typename std::iterator_traits::iterator_category + iterator_category; + + static size_t const size = sizeof(sizer(iterator_category())); + + public: + typedef typename traits_by_size::traits::type type; + }; + } + + template + struct iterator_traits + : public iterator_detail::traits_by_category::type + { + }; +} + +#endif // iterator_suite_rmg_20030823_included diff --git a/include/boost/python/suite/indexing/sample.out b/include/boost/python/suite/indexing/sample.out new file mode 100644 index 00000000..1ac1712e --- /dev/null +++ b/include/boost/python/suite/indexing/sample.out @@ -0,0 +1,44 @@ + +1 2 3 +1 2 3 +copy is 2, v[1] is 2 +copy is 3, v[1] is 3 +copy is 5, v[1] is 5 +slice[0] is 5, copy is 5, v[1] is 5 +slice[0] is 5, copy is 5, v[1] is 6 +slice[0] is 5, copy is 5, v[1] is 7 +slice[0] is 9, copy is 9, v[1] is 7 +============= + +1 2 3 +1 2 3 +copy is 2, v[1] is 2 +copy is 3, v[1] is 3 +copy is 3, v[1] is 5 +slice[0] is 5, copy is 5, v[1] is 5 +slice[0] is 5, copy is 5, v[1] is 6 +slice[0] is 5, copy is 5, v[1] is 7 +slice[0] is 9, copy is 5, v[1] is 7 +============= + +1 2 3 +1 2 3 +copy is 2, v[1] is 2 +copy is 3, v[1] is 3 +copy is 3, v[1] is 5 +slice[0] is 5, copy is 5, v[1] is 5 +slice[0] is 6, copy is 6, v[1] is 6 +slice[0] is 6, copy is 6, v[1] is 7 +slice[0] is 9, copy is 6, v[1] is 7 +============= +[] +1 2 3 +1 2 3 +copy is 2, v[1] is 2 +copy is 3, v[1] is 3 +copy is 3, v[1] is 5 +slice[0] is 5, copy is 5, v[1] is 5 +slice[0] is 6, copy is 6, v[1] is 6 +slice[0] is 6, copy is 6, v[1] is 7 +slice[0] is 9, copy is 6, v[1] is 7 +============= diff --git a/include/boost/python/suite/indexing/shared_proxy_impl.hpp b/include/boost/python/suite/indexing/shared_proxy_impl.hpp new file mode 100755 index 00000000..7c8e7522 --- /dev/null +++ b/include/boost/python/suite/indexing/shared_proxy_impl.hpp @@ -0,0 +1,103 @@ +// -*- mode:c++; switch-modules-target: "container_proxy.hpp" -*- +// +// Header file shared_proxy_impl.hpp +// +// The shared implementation of the element proxy (const and non-const +// versions). Multiple proxy pointers can refer to the same shared +// implementation, meaning that only one object requires updating to +// affect all proxy pointers that ultimately refer to the same index +// in the container. +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this material for any purpose is hereby +// granted without fee, provided the above notices are retained on all +// copies. Permission to modify the material and to distribute modified +// versions is granted, provided the above notices are retained, and a +// notice that the material was modified is included with the above +// copyright notice. +// +// History +// ======= +// 2003/ 8/29 rmg File creation from container_proxy.hh +// +// $Id$ +// + +#ifndef shared_proxy_impl_rmg_20030829_included +#define shared_proxy_impl_rmg_20030829_included + +#include + +template +class shared_proxy_impl +{ +public: + typedef typename ContainerProxy::raw_value_type value_type; + typedef value_type &reference; + typedef value_type *pointer; + typedef void iterator_category; // Fudge for boost::indirect_iterator + typedef void difference_type; // Fudge for boost::indirect_iterator + + reference operator*() const; + pointer operator->() const { return &(**this); } + + ContainerProxy *owner() const { return myOwnerPtr; } + size_t index() const { return myIndex; } + + shared_proxy_impl (value_type const ©); + // Creates value-only (detached) proxy + +private: + template friend class container_proxy; + + shared_proxy_impl (ContainerProxy *, size_t); + void detach (); + +private: + ContainerProxy *myOwnerPtr; // When attached + size_t myIndex; // When attached + std::auto_ptr myElementPtr; // When detached + +private: + // Not implemented + shared_proxy_impl (shared_proxy_impl const &); + shared_proxy_impl &operator= (shared_proxy_impl const &); +}; + +template +shared_proxy_impl::shared_proxy_impl (ContainerProxy *ownerPtr + , size_t index) + : myOwnerPtr (ownerPtr) + , myIndex (index) + , myElementPtr () +{ +} + +template +shared_proxy_impl::shared_proxy_impl (value_type const &val) + : myOwnerPtr (0) + , myIndex (-1) + , myElementPtr (new value_type (val)) +{ +} + +template +typename shared_proxy_impl::reference +shared_proxy_impl::operator* () const +{ + return myOwnerPtr ? myOwnerPtr->container().at (myIndex) : *myElementPtr; +} + +template +void shared_proxy_impl::detach () +{ + myElementPtr.reset (new value_type (**this)); + myOwnerPtr = 0; + myIndex = static_cast(-1); +} + +#endif // shared_proxy_impl_rmg_20030829_included diff --git a/include/boost/python/suite/indexing/suite_utils.hpp b/include/boost/python/suite/indexing/suite_utils.hpp new file mode 100755 index 00000000..c9e21afc --- /dev/null +++ b/include/boost/python/suite/indexing/suite_utils.hpp @@ -0,0 +1,211 @@ +// -*- mode:c++ -*- +// +// Header file suite_utils.hpp +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this material for any purpose is hereby +// granted without fee, provided the above notices are retained on all +// copies. Permission to modify the material and to distribute modified +// versions is granted, provided the above notices are retained, and a +// notice that the material was modified is included with the above +// copyright notice. +// +// History +// ======= +// 2003/ 8/23 rmg File creation +// +// $Id$ +// + +#ifndef suite_utils_rmg_20030823_included +#define suite_utils_rmg_20030823_included + +#include +#include +#include + +namespace indexing { + template + class is_mutable_ref + { + typedef typename boost::remove_reference::type maybe_const; + + public: + static bool const value = ! boost::is_const::value; + }; + + template + struct container_algorithms + { + typedef typename ContainerTraits::container container; + typedef typename ContainerTraits::iterator iterator; + typedef typename ContainerTraits::key_type key_type; + typedef typename ContainerTraits::size_type size_type; + typedef typename ContainerTraits::index_type index_type; + + typedef typename boost::call_traits::param_type key_param; + typedef typename boost::call_traits::param_type index_param; + + static size_type size (container &); + static iterator find (container &, key_param); + static size_type count (container &, key_param); + static void reverse (container &); + static iterator at (container&, index_param); + static void sort (container &); + // static void sort (container &, PyObject *); + }; + + ///////////////////////////////////////////////////////////////////////// + // Get the size of a container + ///////////////////////////////////////////////////////////////////////// + + template + typename container_algorithms::size_type + container_algorithms::size (container &c) + { + return c.size(); + } + + ///////////////////////////////////////////////////////////////////////// + // Find an element in a container (std algorithm version) + ///////////////////////////////////////////////////////////////////////// + + template + typename container_algorithms::iterator + container_algorithms::find (container &c + , key_param key) + { + return std::find (c.begin(), c.end(), key); + } + + ///////////////////////////////////////////////////////////////////////// + // Count occurances of an element in a container (std algorithm version) + ///////////////////////////////////////////////////////////////////////// + + template + typename container_algorithms::size_type + container_algorithms::count (container &c + , key_param key) + { + return std::count (c.begin(), c.end(), key); + } + + ///////////////////////////////////////////////////////////////////////// + // Index into a container (iterator version) + ///////////////////////////////////////////////////////////////////////// + + template + typename container_algorithms::iterator + container_algorithms::at (container &c, index_param ix) + { + return c.begin() + ix; + } + + ///////////////////////////////////////////////////////////////////////// + // Reverse the contents of a container (std algorithm version) + ///////////////////////////////////////////////////////////////////////// + + template + void container_algorithms::reverse (container &c) + { + std::reverse (c.begin(), c.end()); + } + + ///////////////////////////////////////////////////////////////////////// + // Sort the contents of a container (std algorithm version) + ///////////////////////////////////////////////////////////////////////// + + template + void container_algorithms::sort (container &c) + { + std::sort (c.begin(), c.end()); + } + + ///////////////////////////////////////////////////////////////////////// + // Special cases for std::list + ///////////////////////////////////////////////////////////////////////// + + template + struct list_algorithms : public container_algorithms + { + private: + typedef container_algorithms Parent; + + public: + typedef typename Parent::container container; + + // Use member functions for the following (hiding base class versions) + static void reverse (container &); + static void sort (container &); + // static void sort (container &, PyObject *); + }; + + ///////////////////////////////////////////////////////////////////////// + // Reverse the contents of a list (member function version) + ///////////////////////////////////////////////////////////////////////// + + template + void list_algorithms::reverse (container &c) + { + c.reverse(); + } + + ///////////////////////////////////////////////////////////////////////// + // Sort the contents of a container (std algorithm version) + ///////////////////////////////////////////////////////////////////////// + + template + void list_algorithms::sort (container &c) + { + c.sort(); + } + + ///////////////////////////////////////////////////////////////////////// + // Special cases for associative containers + ///////////////////////////////////////////////////////////////////////// + + template + struct assoc_algorithms : public container_algorithms + { + private: + typedef container_algorithms Parent; + + public: + typedef typename Parent::iterator iterator; + typedef typename Parent::size_type size_type; + typedef typename Parent::container container; + typedef typename Parent::key_param key_param; + + // Use member functions for the following (hiding base class versions) + static iterator find (container &, key_param); + static size_type count (container &, key_param); + }; + + ///////////////////////////////////////////////////////////////////////// + // Find an element in an associative container + ///////////////////////////////////////////////////////////////////////// + + template + typename assoc_algorithms::iterator + assoc_algorithms::find (container &c, key_param key) + { + return c.find (key); + } + + ///////////////////////////////////////////////////////////////////////// + // Count occurances of an element in a container (std algorithm version) + ///////////////////////////////////////////////////////////////////////// + + template + typename assoc_algorithms::size_type + assoc_algorithms::count (container &c, key_param key) + { + return c.count (key); + } +} + +#endif // suite_utils_rmg_20030823_included diff --git a/include/boost/python/suite/indexing/testcontprox.cpp b/include/boost/python/suite/indexing/testcontprox.cpp new file mode 100755 index 00000000..02c8fd23 --- /dev/null +++ b/include/boost/python/suite/indexing/testcontprox.cpp @@ -0,0 +1,145 @@ +// -*- mode:c++; switch-modules-target: "container_proxy.hpp" -*- +// +// Module testcontprox.cpp +// +// Copyright (c) 2003 Raoul M. Gough +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this material for any purpose is hereby +// granted without fee, provided the above notices are retained on all +// copies. Permission to modify the material and to distribute modified +// versions is granted, provided the above notices are retained, and a +// notice that the material was modified is included with the above +// copyright notice. +// +// History +// ======= +// 2003/ 8/26 rmg File creation +// +// $Id$ +// + +#include "container_proxy.hpp" +#include +#include +#include +#include +#include +#include +#include + +int unique_int() { + static int counter = 400; + + return counter++; +} + +int main () +{ + int i = unique_int(); + typedef std::vector Container; + typedef container_proxy > ContainerProxy; + + Container raw_container; + raw_container.push_back (i); + + ContainerProxy proxy (&raw_container); + + std::cout + << proxy.container().front() // Should be first element + << " " + << *proxy.at (0) // Should be the same + << "\n"; + + ContainerProxy::value_type ptr (proxy.at (0)); + + std::cout + << *ptr // Should be first element + << " " + << ptr.use_count(); // Should be 2 (internal map has a ref) + + { + ContainerProxy::const_value_type + const_ptr (const_cast(proxy).at(0)); + + std::cout + << ", " + << *const_ptr // Should also be first element + << " " + << ptr.use_count(); // Should be 3 (two proxies and internal) + } + + proxy.replace (0, unique_int()); + + std::cout + << ", " + << *ptr // Should be *original* first element + << " " + << ptr.use_count() // Should be 1 (detached) + << ", " + << proxy.at (0) // Should be new first element + << "\n"; + + ptr = proxy.at (0); // Proxy to new first element (attached) + + proxy.insert (proxy.begin(), unique_int()); + proxy.insert (proxy.begin(), unique_int()); + + int array[4]; + for (int count = 0; count < 4; ++count) array[count] = unique_int(); + + proxy.insert (proxy.begin(), array, array + 4); + + std::cout << "(" << *ptr << ", " << ptr.use_count() << ") "; + std::copy (raw_container.begin(), raw_container.end() + , std::ostream_iterator(std::cout, " ")); + std::cout << "\n"; + + proxy.erase (proxy.begin()); + proxy.erase (proxy.begin(), proxy.begin() + 3); + + std::cout << "(" << *ptr << ", " << ptr.use_count() << ") "; + std::copy (raw_container.begin(), raw_container.end() + , std::ostream_iterator(std::cout, " ")); + std::cout << "\n"; + + std::stringstream strm; + strm << unique_int(); + strm << " " << unique_int(); + proxy.insert (proxy.begin() + , std::istream_iterator (strm) + , std::istream_iterator ()); + + std::cout << "(" << *ptr << ", " << ptr.use_count() << ") "; + std::copy (raw_container.begin(), raw_container.end() + , std::ostream_iterator(std::cout, " ")); + std::cout << "\n"; + + // Example set_item implementation - replacing the slice [1:4] with a + // single element + proxy.detach_proxy (1); + proxy.at(1) = unique_int(); // Replace with new element + proxy.erase (proxy.begin() + 2, proxy.begin() + 4); + + std::cout << "(" << *ptr << ", " << ptr.use_count() << ") "; + + + // Try indirect_iterator just for fun, but this creates proxies for + // every element in the container! + + using boost::make_indirect_iterator; + + std::copy (make_indirect_iterator(proxy.begin()) + , make_indirect_iterator(proxy.end()) + , std::ostream_iterator(std::cout, " ")); + + std::cout << "\n"; + + // Try erasing the whole container, make sure proxy has value and is unique + proxy.erase (proxy.begin(), proxy.end()); + std::cout + << "(" << *ptr << ", " << ptr.use_count() << ") " + << proxy.size() << "\n"; +} diff --git a/include/boost/python/suite/indexing/testindexing.py b/include/boost/python/suite/indexing/testindexing.py new file mode 100755 index 00000000..c7b686ea --- /dev/null +++ b/include/boost/python/suite/indexing/testindexing.py @@ -0,0 +1,70 @@ +#!/usr/bin/python +# -*- mode:python; switch-modules-target: "indexing.cpp" -*- +# +# Python module testindexing.py +# +# Copyright (c) 2003 Raoul M. Gough +# +# This material is provided "as is", with absolutely no warranty expressed +# or implied. Any use is at your own risk. +# +# Permission to use or copy this software for any non-commercial purpose +# is hereby granted without fee, provided the above notices are retained +# on all copies. Permission to modify the code and to distribute +# modified code for non-commercial purposes is granted, provided the +# above notices are retained, and a notice that the code was modified is +# included with the above copyright notice. +# +# History +# ======= +# 2003/ 8/19 rmg File creation +# +# $Id$ +# + +from indexing import * + +trace(0) + +def test_from_empty(v): +# v.reserve (10); + + print v + + v.append (IntWrapper (1)) + v.append (IntWrapper (2)) + v.append (IntWrapper (3)) + + print v[0], v[1], v[2] + + try: + print v[0], v[1:][0], v[-1:][0] + + except Exception, e: + print e + pass + + copy = v[1] + + print "copy is %s, v[1] is %s" % (copy, v[1]) + v[1].increment() + print "copy is %s, v[1] is %s" % (copy, v[1]) + v[1] = IntWrapper (5) + print "copy is %s, v[1] is %s" % (copy, v[1]) + + slice = v[1:2] + copy = slice[0] + print "slice[0] is %s, copy is %s, v[1] is %s" % (slice[0], copy, v[1]) + v[1].increment() + print "slice[0] is %s, copy is %s, v[1] is %s" % (slice[0], copy, v[1]) + v[1] = IntWrapper (7) + print "slice[0] is %s, copy is %s, v[1] is %s" % (slice[0], copy, v[1]) + slice[0] = IntWrapper (9) + print "slice[0] is %s, copy is %s, v[1] is %s" % (slice[0], copy, v[1]) + + print "=============" + +test_from_empty (Vector()) +test_from_empty (ProxyVector()) +test_from_empty (PointerVector()) +test_from_empty ([]) diff --git a/include/boost/python/suite/indexing/testlist.py b/include/boost/python/suite/indexing/testlist.py new file mode 100755 index 00000000..96a1efe2 --- /dev/null +++ b/include/boost/python/suite/indexing/testlist.py @@ -0,0 +1,158 @@ +#!/usr/bin/python +# -*- mode:python -*- +# +# Python module testlist.py +# +# Copyright (c) 2003 Raoul M. Gough +# +# This material is provided "as is", with absolutely no warranty expressed +# or implied. Any use is at your own risk. +# +# Permission to use or copy this material for any purpose is hereby +# granted without fee, provided the above notices are retained on all +# copies. Permission to modify the material and to distribute modified +# versions is granted, provided the above notices are retained, and a +# notice that the material was modified is included with the above +# copyright notice. +# +# History +# ======= +# 2003/ 8/20 rmg File creation +# +# $Id$ +# + +from types import SliceType + +class ListIter: + def __init__(self, l): + self.myList = l + self.myIndex = 0 + + def next(self): + print "next(ListIter)", + try: + result = self.myList[self.myIndex] + self.myIndex += 1 + print "ok" + return result + + except IndexError: + print "StopIteration" + raise StopIteration + +class MyList: + def __init__(self, l = None): + print "__init__(MyList)" + + if l == None: + self.myList = list() + else: + self.myList = l + + def __setitem__(self, key, value): + print "__setitem__(MyList, %s, %s)" % (key, value) + if isinstance (key, SliceType): + if key.step != None: + raise TypeError ("Invalid slice for list") + self.myList.__setslice__ (key.start, key.stop, value) + + else: + self.myList.__setitem__(key, value) + + def __iter__(self): + print "__iter__(MyList)" + return ListIter(self.myList) + + def __getitem__(self, key): + print "__getitem__(MyList, %s)" % key + if isinstance (key, SliceType): + if key.step != None: + raise TypeError ("Invalid slice for list") + return self.myList.__getslice__ (key.start, key.stop) + return self.myList.__getitem__(key) + + def __delitem__(self, key): + print "__delitem__(MyList, %s)" % key + if isinstance (key, SliceType): + if key.step != None: + raise TypeError ("Invalid slice for list") + self.myList.__delslice__ (key.start, key.stop) + + else: + self.myList.__delitem__(key) + + def __len__(self): + print "__len__(MyList)" + return self.myList.__len__() + + def __add__(self, value): + print "__add__(MyList, %s)" % value + return MyList(self.myList.__add__(value)) + + def __mul__(self, value): + print "__mul__(MyList, %s)" % value + return MyList(self.myList.__mul__(value)) + +# def __imul__(self, value): +# print "__imul__(MyList, %s)" % value +# self.myList.__imul__(value) + +# def __contains__(self, value): +# print "__contains__(MyList, %s)" % value +# return self.myList.__contains__(value) + + def append(self, value): + print "append(MyList, %s)" % value + self.myList.append(value) + + def __insert__(self, key, value): + print "__insert__(MyList, %s, %s)" % (key, value) + self.myList.__insert__(key, value) + +# def __setslice__(self, start, end, value): +# print "__setslice__(MyList, %s, %s, %s)" % (start, end, value) +# self.myList.__setslice__(start, end, value) + +# def __getslice__(self, start, end): +# print "__getslice__(MyList, %s, %s)" % (start, end) +# self.myList.__getslice__(start, end) + +# def __delslice__(self, start, end): +# print "__delslice__(MyList, %s, %s)" % (start, end) +# self.myList.__delslice__(start, end) + + def __repr__(self): + return self.myList.__repr__() + +x = MyList() + +x.append(4) +x.append(5) +x.append(6) + +print 4 in x +print 7 in x + +x[1] = 0 +x[2:3]=[7, 8, 9] +print x + [10] +print x[2:] + +del x[2:3] +del x[0] + +print x + +x[1:2] = [] + +print x + +x *= 2 + +print x + +x[0:3][0:1] = [1] + +print x + diff --git a/include/boost/python/suite/indexing/vector_indexing_suite.py b/include/boost/python/suite/indexing/vector_indexing_suite.py new file mode 100755 index 00000000..1905d7df --- /dev/null +++ b/include/boost/python/suite/indexing/vector_indexing_suite.py @@ -0,0 +1,344 @@ +''' + +##################################################################### +# Check an object that we will use as container element +##################################################################### + +>>> from vector_indexing_suite_ext import * +>>> x = X('hi') +>>> x +hi +>>> x.reset() # a member function that modifies X +>>> x +reset +>>> x.foo() # another member function that modifies X +>>> x +foo + +# test that a string is implicitly convertible +# to an X +>>> x_value('bochi bochi') +'gotya bochi bochi' + +##################################################################### +# Iteration +##################################################################### +>>> def print_xvec(xvec): +... s = '[ ' +... for x in xvec: +... s += repr(x) +... s += ' ' +... s += ']' +... print s + +##################################################################### +# Replace all the contents using slice syntax +##################################################################### + +>>> v = XVec() +>>> v[:] = [X('a'),X('b'),X('c'),X('d'),X('e')] +>>> print_xvec(v) +[ a b c d e ] + +##################################################################### +# Indexing +##################################################################### +>>> len(v) +5 +>>> v[0] +a +>>> v[1] +b +>>> v[2] +c +>>> v[3] +d +>>> v[4] +e +>>> v[-1] +e +>>> v[-2] +d +>>> v[-3] +c +>>> v[-4] +b +>>> v[-5] +a + +##################################################################### +# Deleting an element +##################################################################### + +>>> del v[0] +>>> v[0] = 'yaba' # must do implicit conversion +>>> print_xvec(v) +[ yaba c d e ] + +##################################################################### +# Calling a mutating function of a container element +##################################################################### +>>> v[3].reset() +>>> v[3] +reset + +##################################################################### +# Copying a container element +##################################################################### +>>> x = X(v[3]) +>>> x +reset +>>> x.foo() +>>> x +foo +>>> v[3] # should not be changed to 'foo' +reset + +##################################################################### +# Referencing a container element +##################################################################### +>>> x = v[3] +>>> x +reset +>>> x.foo() +>>> x +foo +>>> v[3] # should be changed to 'foo' +foo + +##################################################################### +# Slice +##################################################################### + +>>> sl = v[0:2] +>>> print_xvec(sl) +[ yaba c ] +>>> sl[0].reset() +>>> sl[0] +reset + +##################################################################### +# Reset the container again +##################################################################### +>>> v[:] = ['a','b','c','d','e'] # perform implicit conversion to X +>>> print_xvec(v) +[ a b c d e ] + +##################################################################### +# Slice: replace [1:3] with an element +##################################################################### +>>> v[1:3] = X('z') +>>> print_xvec(v) +[ a z d e ] + +##################################################################### +# Slice: replace [0:2] with a list +##################################################################### +>>> v[0:2] = ['1','2','3','4'] # perform implicit conversion to X +>>> print_xvec(v) +[ 1 2 3 4 d e ] + +##################################################################### +# Slice: delete [3:4] +##################################################################### +>>> del v[3:4] +>>> print_xvec(v) +[ 1 2 3 d e ] + +##################################################################### +# Slice: set [3:] to a list +##################################################################### +>>> v[3:] = [X('trailing'), X('stuff')] # a list +>>> print_xvec(v) +[ 1 2 3 trailing stuff ] + +##################################################################### +# Slice: delete [:3] +##################################################################### +>>> del v[:3] +>>> print_xvec(v) +[ trailing stuff ] + +##################################################################### +# Slice: insert a tuple to [0:0] +##################################################################### +>>> v[0:0] = ('leading','stuff') # can also be a tuple +>>> print_xvec(v) +[ leading stuff trailing stuff ] + +##################################################################### +# Reset the container again +##################################################################### +>>> v[:] = ['a','b','c','d','e'] + +##################################################################### +# Some references to the container elements +##################################################################### +>>> z0 = v[0] +>>> z1 = v[1] +>>> z2 = v[2] +>>> z3 = v[3] +>>> z4 = v[4] + +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy +c +>>> z3 # proxy +d +>>> z4 # proxy +e + +##################################################################### +# Delete a container element +##################################################################### + +>>> del v[2] +>>> print_xvec(v) +[ a b d e ] + +##################################################################### +# Show that the references are still valid +##################################################################### +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy detached +c +>>> z3 # proxy index adjusted +d +>>> z4 # proxy index adjusted +e + +##################################################################### +# Delete all container elements +##################################################################### +>>> del v[:] +>>> print_xvec(v) +[ ] + +##################################################################### +# Show that the references are still valid +##################################################################### +>>> z0 # proxy detached +a +>>> z1 # proxy detached +b +>>> z2 # proxy detached +c +>>> z3 # proxy detached +d +>>> z4 # proxy detached +e + +##################################################################### +# Reset the container again +##################################################################### +>>> v[:] = ['a','b','c','d','e'] + +##################################################################### +# renew the references to the container elements +##################################################################### +>>> z0 = v[0] +>>> z1 = v[1] +>>> z2 = v[2] +>>> z3 = v[3] +>>> z4 = v[4] + +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy +c +>>> z3 # proxy +d +>>> z4 # proxy +e + +##################################################################### +# Set [2:4] to a list such that there will be more elements +##################################################################### +>>> v[2:4] = ['x','y','v'] +>>> print_xvec(v) +[ a b x y v e ] + +##################################################################### +# Show that the references are still valid +##################################################################### +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy detached +c +>>> z3 # proxy detached +d +>>> z4 # proxy index adjusted +e + +##################################################################### +# Contains +##################################################################### +>>> v[:] = ['a','b','c','d','e'] # reset again + +>>> assert 'a' in v +>>> assert 'b' in v +>>> assert 'c' in v +>>> assert 'd' in v +>>> assert 'e' in v +>>> assert not 'X' in v +>>> assert not 12345 in v + +##################################################################### +# Show that iteration allows mutable access to the elements +##################################################################### +>>> v[:] = ['a','b','c','d','e'] # reset again +>>> for x in v: +... x.reset() +>>> print_xvec(v) +[ reset reset reset reset reset ] + +##################################################################### +# append +##################################################################### +>>> v[:] = ['a','b','c','d','e'] # reset again +>>> v.append('f') +>>> print_xvec(v) +[ a b c d e f ] + +##################################################################### +# extend +##################################################################### +>>> v[:] = ['a','b','c','d','e'] # reset again +>>> v.extend(['f','g','h','i','j']) +>>> print_xvec(v) +[ a b c d e f g h i j ] + +##################################################################### +# END.... +##################################################################### + +''' + + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print 'running...' + import sys + sys.exit(run()[0]) + + + + + diff --git a/include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp b/include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp new file mode 100755 index 00000000..086d000b --- /dev/null +++ b/include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include "container_proxy.hpp" + +using namespace boost::python; + +struct X // a container element +{ + std::string s; + X():s("default") {} + X(std::string s):s(s) {} + std::string repr() const { return s; } + void reset() { s = "reset"; } + void foo() { s = "foo"; } + bool operator==(X const& x) const { return s == x.s; } + bool operator!=(X const& x) const { return s != x.s; } +}; + +std::string x_value(X const& x) +{ + return "gotya " + x.s; +} + +X *get_pointer (container_proxy >::value_type const &proxy) +{ + return &(*proxy); +} + +BOOST_PYTHON_MODULE(vector_indexing_suite_ext) +{ + class_("X") + .def(init<>()) + .def(init()) + .def(init()) + .def("__repr__", &X::repr) + .def("reset", &X::reset) + .def("foo", &X::foo) + ; + + def("x_value", x_value); + implicitly_convertible(); + + typedef std::vector RawContainer; + typedef container_proxy Container; + + boost::python::register_ptr_to_python(); + + implicitly_convertible(); + + class_("XVec") + .def(vector_indexing_suite()) + ; + + // Compile check only... + class_ >("FloatVec") + .def(vector_indexing_suite >()) + ; +} + diff --git a/include/boost/python/suite/indexing/vis.hpp.patch b/include/boost/python/suite/indexing/vis.hpp.patch new file mode 100644 index 00000000..8a8b429a --- /dev/null +++ b/include/boost/python/suite/indexing/vis.hpp.patch @@ -0,0 +1,33 @@ +Index: vector_indexing_suite.hpp +=================================================================== +RCS file: /cvsroot/boost/boost/boost/python/suite/indexing/vector_indexing_suite.hpp,v +retrieving revision 1.4 +diff -w -d -u -r1.4 vector_indexing_suite.hpp +--- vector_indexing_suite.hpp 14 Aug 2003 12:14:25 -0000 1.4 ++++ vector_indexing_suite.hpp 30 Aug 2003 11:47:22 -0000 +@@ -52,6 +52,7 @@ + public: + + typedef typename Container::value_type data_type; ++ typedef typename Container::reference reference_type; + typedef typename Container::value_type key_type; + typedef typename Container::size_type index_type; + typedef typename Container::size_type size_type; +@@ -67,7 +68,7 @@ + ; + } + +- static data_type& ++ static reference_type + get_item(Container& container, index_type i) + { + return container[i]; +@@ -185,7 +186,7 @@ + static void + base_append(Container& container, PyObject* v) + { +- extract elem(v); ++ extract elem(v); + // try if elem is an exact Data + if (elem.check()) + {