diff --git a/include/boost/python/suite/indexing/IntWrapper.cpp b/include/boost/python/suite/indexing/IntWrapper.cpp index f5c6dc04..2830db91 100755 --- a/include/boost/python/suite/indexing/IntWrapper.cpp +++ b/include/boost/python/suite/indexing/IntWrapper.cpp @@ -121,6 +121,24 @@ bool operator< (IntWrapper const &lhs, IntWrapper const &rhs) return lhs.mI < rhs.mI; } +int compare (IntWrapper const &lhs, IntWrapper const &rhs) +{ + if (lhs < rhs) + { + return -1; + } + + else if (rhs < lhs) + { + return 1; + } + + else + { + return 0; + } +} + std::ostream &operator<< (std::ostream &strm, IntWrapper const &iw) { strm << iw.mI; diff --git a/include/boost/python/suite/indexing/IntWrapper.hpp b/include/boost/python/suite/indexing/IntWrapper.hpp index c992063f..70c37c30 100755 --- a/include/boost/python/suite/indexing/IntWrapper.hpp +++ b/include/boost/python/suite/indexing/IntWrapper.hpp @@ -48,6 +48,7 @@ struct IntWrapper { bool operator== (IntWrapper const &lhs, IntWrapper const &rhs); bool operator< (IntWrapper const &lhs, IntWrapper const &rhs); +int compare (IntWrapper const &lhs, IntWrapper const &rhs); std::ostream &operator<< (std::ostream &strm, IntWrapper const &iw); #endif // IntWrapper_rmg_20030910_included diff --git a/include/boost/python/suite/indexing/algo_selector.hpp b/include/boost/python/suite/indexing/algo_selector.hpp index 5244fc77..2693cf58 100755 --- a/include/boost/python/suite/indexing/algo_selector.hpp +++ b/include/boost/python/suite/indexing/algo_selector.hpp @@ -33,6 +33,7 @@ // Definitions of supported types #include "iterator_pair.hpp" +#include "container_proxy.hpp" #include #include #include @@ -164,6 +165,20 @@ namespace indexing { typedef default_algorithms mutable_algorithms; typedef default_algorithms const_algorithms; }; + + // Container proxies + template + class selector_impl > + { + typedef container_proxy Container; + + typedef container_proxy_traits mutable_traits; + typedef container_proxy_traits const_traits; + + public: + typedef default_algorithms mutable_algorithms; + typedef default_algorithms const_algorithms; + }; } // Select the right algorithms for each supported kind of container diff --git a/include/boost/python/suite/indexing/algorithms.hpp b/include/boost/python/suite/indexing/algorithms.hpp index 7c44582c..84073c50 100755 --- a/include/boost/python/suite/indexing/algorithms.hpp +++ b/include/boost/python/suite/indexing/algorithms.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace indexing { @@ -44,8 +45,8 @@ namespace indexing { typedef typename ContainerTraits::reference reference; typedef typename ContainerTraits::key_type key_type; typedef typename ContainerTraits::size_type size_type; - typedef typename ContainerTraits::index_type index_type; typedef typename ContainerTraits::value_type value_type; + typedef long index_type; // Ignore container traits (need signed repr.) typedef typename boost::call_traits::param_type value_param; typedef typename boost::call_traits::param_type key_param; @@ -58,132 +59,26 @@ namespace indexing { static reference get (container &, index_param); static void assign (container &, index_param, value_param); static void insert (container &, index_param, value_param); - static void erase (container &, index_param, index_param); + static void erase_one (container &, index_param); + static void erase_range(container &, index_param, index_param); static void push_back (container &, value_param); static void sort (container &); // static void sort (container &, PyObject *); static iterator begin (container &c) { return c.begin(); } static iterator end (container &c) { return c.end(); } + + template + static void visitor_helper (PythonClass &, Policy const &); + + private: + static size_type bounds_check (container &, index_param, char const *msg + , bool one_past = false); + // Throws std::out_of_range if necessary. If one_past is set, then + // indexes up to container.size() *inclusive* are allowed. Otherwise + // returns a normalized index. }; - ///////////////////////////////////////////////////////////////////////// - // Get the size of a container - ///////////////////////////////////////////////////////////////////////// - - template - typename default_algorithms::size_type - default_algorithms::size (container &c) - { - return c.size(); - } - - ///////////////////////////////////////////////////////////////////////// - // Find an element in a container (std algorithm version) - ///////////////////////////////////////////////////////////////////////// - - template - typename default_algorithms::iterator - default_algorithms::find (container &c - , key_param key) - { - return std::find (begin(c), end(c), key); - } - - ///////////////////////////////////////////////////////////////////////// - // Count occurances of an element in a container (std algorithm version) - ///////////////////////////////////////////////////////////////////////// - - template - typename default_algorithms::size_type - default_algorithms::count (container &c - , key_param key) - { - return std::count (begin(c), end(c), key); - } - - ///////////////////////////////////////////////////////////////////////// - // Index into a container (generic version) - ///////////////////////////////////////////////////////////////////////// - - template - typename default_algorithms::reference - default_algorithms::get (container &c, index_param ix) - { - return c.at (ix); - } - - ///////////////////////////////////////////////////////////////////////// - // Assign a value at a particular index (generic version) - ///////////////////////////////////////////////////////////////////////// - - template - void - default_algorithms::assign (container &c - , index_param ix - , value_param val) - { - c.at(ix) = val; - } - - ///////////////////////////////////////////////////////////////////////// - // Insert at end of a container (generic version) - ///////////////////////////////////////////////////////////////////////// - - template - void - default_algorithms::push_back (container &c - , value_param v) - { - c.push_back (v); - } - - ///////////////////////////////////////////////////////////////////////// - // Insert at an index in the container (generic version) - ///////////////////////////////////////////////////////////////////////// - - template - void - default_algorithms::insert (container &c - , index_param i - , value_param v) - { - c.insert (c.begin() + i, v); - } - - ///////////////////////////////////////////////////////////////////////// - // Erase between given indexes in the container (generic version) - ///////////////////////////////////////////////////////////////////////// - - template - void - default_algorithms::erase (container &c - , index_param from - , index_param to) - { - c.erase (c.begin() + from, c.begin() + to); - } - - ///////////////////////////////////////////////////////////////////////// - // Reverse the contents of a container (std algorithm version) - ///////////////////////////////////////////////////////////////////////// - - template - void default_algorithms::reverse (container &c) - { - std::reverse (begin(c), end(c)); - } - - ///////////////////////////////////////////////////////////////////////// - // Sort the contents of a container (std algorithm version) - ///////////////////////////////////////////////////////////////////////// - - template - void default_algorithms::sort (container &c) - { - std::sort (begin(c), end(c)); - } - ///////////////////////////////////////////////////////////////////////// // Special cases for std::list ///////////////////////////////////////////////////////////////////////// @@ -203,26 +98,6 @@ namespace indexing { // 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 ///////////////////////////////////////////////////////////////////////// @@ -239,73 +114,300 @@ namespace indexing { typedef typename Parent::container container; typedef typename Parent::reference reference; typedef typename Parent::key_param key_param; - typedef typename Parent::index_param index_param; typedef typename Parent::value_param value_param; + // Use the trait's index type information to support non-integer indexes + typedef typename ContainerTraits::index_type index_type; + typedef typename boost::call_traits::param_type index_param; + static reference get (container &, index_param); static void assign (container &, index_param, value_param); // Use member functions for the following (hiding base class versions) + static void erase_one (container &, key_param); static iterator find (container &, key_param); static size_type count (container &, key_param); }; +} - ///////////////////////////////////////////////////////////////////////// - // Index into a container (associative version) - ///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// Get the size of a container +///////////////////////////////////////////////////////////////////////// - template - typename assoc_algorithms::reference - assoc_algorithms::get (container &c, index_param ix) - { - iterator iter = find (c, ix); +template +typename indexing::default_algorithms::size_type +indexing::default_algorithms::size (container &c) +{ + return c.size(); +} - if (iter == end(c)) - { - throw std::domain_error - (std::string ("associative container: key not found")); - } +///////////////////////////////////////////////////////////////////////// +// Range check an index and throw out_of_range if necessary +///////////////////////////////////////////////////////////////////////// - else - { - return iter->second; - } - } +template +typename indexing::default_algorithms::size_type +indexing::default_algorithms::bounds_check (container &c + , index_param ix + , char const *msg + , bool one_past) +{ + size_type bound = size(c) + (one_past ? 1 : 0); + size_type result; - ///////////////////////////////////////////////////////////////////////// - // Assign a value at a particular index (associative version) - ///////////////////////////////////////////////////////////////////////// + if (ix < 0) + { + if (size_type(-ix) > bound) + { + throw std::out_of_range (msg); + } - template - void - assoc_algorithms::assign (container &c - , index_param ix - , value_param val) - { - c[ix] = val; - } + result = bound + ix; + } - ///////////////////////////////////////////////////////////////////////// - // Find an element in an associative container - ///////////////////////////////////////////////////////////////////////// + else + { + result = ix; + } - template - typename assoc_algorithms::iterator - assoc_algorithms::find (container &c, key_param key) - { - return c.find (key); - } + if (result >= bound) + { + throw std::out_of_range (msg); + } - ///////////////////////////////////////////////////////////////////////// - // Count occurances of an element in a container (associative version) - ///////////////////////////////////////////////////////////////////////// + return result; +} - template - typename assoc_algorithms::size_type - assoc_algorithms::count (container &c, key_param key) - { - return c.count (key); - } +///////////////////////////////////////////////////////////////////////// +// Find an element in a container (std algorithm version) +///////////////////////////////////////////////////////////////////////// + +template +typename indexing::default_algorithms::iterator +indexing::default_algorithms::find (container &c + , key_param key) +{ + return std::find (begin(c), end(c), key); +} + +///////////////////////////////////////////////////////////////////////// +// Count occurances of an element in a container (std algorithm version) +///////////////////////////////////////////////////////////////////////// + +template +typename indexing::default_algorithms::size_type +indexing::default_algorithms::count (container &c + , key_param key) +{ + return std::count (begin(c), end(c), key); +} + +///////////////////////////////////////////////////////////////////////// +// Index into a container (generic version) +///////////////////////////////////////////////////////////////////////// + +template +typename indexing::default_algorithms::reference +indexing::default_algorithms::get (container &c + , index_param ix) +{ + return c[bounds_check (c, ix, "get")]; +} + +///////////////////////////////////////////////////////////////////////// +// Assign a value at a particular index (generic version) +///////////////////////////////////////////////////////////////////////// + +template +void +indexing::default_algorithms::assign (container &c + , index_param ix + , value_param val) +{ + c[bounds_check (c, ix, "assign")] = val; +} + +///////////////////////////////////////////////////////////////////////// +// Insert at end of a container (generic version) +///////////////////////////////////////////////////////////////////////// + +template +void +indexing::default_algorithms::push_back (container &c + , value_param v) +{ + c.push_back (v); +} + +///////////////////////////////////////////////////////////////////////// +// Insert at an index in the container (generic version) +///////////////////////////////////////////////////////////////////////// + +template +void +indexing::default_algorithms::insert (container &c + , index_param i + , value_param v) +{ + // Index may range up to c.size() inclusive to allow inserting at end + c.insert (c.begin() + bounds_check (c, i, "insert", true), v); +} + +///////////////////////////////////////////////////////////////////////// +// Erase between given indexes in the container (generic version) +///////////////////////////////////////////////////////////////////////// + +template +void +indexing::default_algorithms::erase_range (container &c + , index_param from + , index_param to) +{ + // End index is one-past-the-end, so may range up to c.size() inclusive + + c.erase (c.begin() + bounds_check (c, from, "erase_range (from)") + , c.begin() + bounds_check (c, to, "erase_range (to)", true)); +} + +///////////////////////////////////////////////////////////////////////// +// Erase one element at the given index in the container (generic version) +///////////////////////////////////////////////////////////////////////// + +template +void +indexing::default_algorithms::erase_one (container &c + , index_param ix) +{ + c.erase (c.begin() + bounds_check (c, ix, "erase_one")); +} + +///////////////////////////////////////////////////////////////////////// +// Reverse the contents of a container (std algorithm version) +///////////////////////////////////////////////////////////////////////// + +template +void indexing::default_algorithms::reverse (container &c) +{ + std::reverse (begin(c), end(c)); +} + +///////////////////////////////////////////////////////////////////////// +// Sort the contents of a container (std algorithm version) +///////////////////////////////////////////////////////////////////////// + +template +void indexing::default_algorithms::sort (container &c) +{ + std::sort (begin(c), end(c)); +} + +///////////////////////////////////////////////////////////////////////// +// Visitor helper function (default version) +///////////////////////////////////////////////////////////////////////// + +template +template +void +indexing::default_algorithms +::visitor_helper (PythonClass &pyClass, Policy const &policy) +{ + container_traits::visitor_helper (pyClass, policy); +} + +///////////////////////////////////////////////////////////////////////// +// Reverse the contents of a list (member function version) +///////////////////////////////////////////////////////////////////////// + +template +void indexing::list_algorithms::reverse (container &c) +{ + c.reverse(); +} + +///////////////////////////////////////////////////////////////////////// +// Sort the contents of a container (std algorithm version) +///////////////////////////////////////////////////////////////////////// + +template +void indexing::list_algorithms::sort (container &c) +{ + c.sort(); +} + +///////////////////////////////////////////////////////////////////////// +// Index into a container (associative version) +///////////////////////////////////////////////////////////////////////// + +template +typename indexing::assoc_algorithms::reference +indexing::assoc_algorithms::get (container &c, index_param ix) +{ + iterator iter = find (c, ix); + + if (iter == end(c)) + { + // ? Is domain_error appropriate here? + throw std::domain_error + (std::string ("associative container get: key not found")); + } + + else + { + return iter->second; + } +} + +///////////////////////////////////////////////////////////////////////// +// Erase elements with the given key (associative version) +///////////////////////////////////////////////////////////////////////// + +template +void +indexing::assoc_algorithms::erase_one (container &c + , key_param key) +{ + if (c.erase (key) == 0) + { + // ? Is domain_error appropriate here? + throw std::domain_error + (std::string ("associative container erase: key not found")); + } +} + +///////////////////////////////////////////////////////////////////////// +// Assign a value at a particular index (associative version) +///////////////////////////////////////////////////////////////////////// + +template +void +indexing::assoc_algorithms::assign (container &c + , index_param ix + , value_param val) +{ + c[ix] = val; +} + +///////////////////////////////////////////////////////////////////////// +// Find an element in an associative container +///////////////////////////////////////////////////////////////////////// + +template +typename indexing::assoc_algorithms::iterator +indexing::assoc_algorithms::find (container &c, key_param key) +{ + return c.find (key); +} + +///////////////////////////////////////////////////////////////////////// +// Count occurances of an element in a container (associative version) +///////////////////////////////////////////////////////////////////////// + +template +typename indexing::assoc_algorithms::size_type +indexing::assoc_algorithms::count (container &c + , key_param key) +{ + return c.count (key); } #endif // algorithms_rmg_20030823_included diff --git a/include/boost/python/suite/indexing/container_proxy.hpp b/include/boost/python/suite/indexing/container_proxy.hpp index 55394428..fed1c2b3 100755 --- a/include/boost/python/suite/indexing/container_proxy.hpp +++ b/include/boost/python/suite/indexing/container_proxy.hpp @@ -25,6 +25,7 @@ #define container_proxy_rmg_20030826_included #include "shared_proxy_impl.hpp" +#include "element_proxy.hpp" #include #include #include @@ -60,139 +61,20 @@ public: typedef typename Container::value_type raw_value_type; - struct const_element_proxy; + typedef element_proxy value_type; + typedef value_type reference; // Already has ref. semantics - 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; + typedef const_element_proxy const_value_type; + typedef const_value_type const_reference; // Has ref. semantics 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 + typedef container_proxy::value_type value_type; + typedef value_type *pointer; + typedef value_type reference; // Already has reference semantics iterator (container_proxy *p, size_type i) : ptr (p), index (i) { } @@ -246,11 +128,11 @@ public: 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; + reference at (size_type index); + const_reference 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); } + reference operator[] (size_type index) { return at(index); } + const_reference operator[] (size_type index) const { return at(index); } size_type size() const { return container().size(); } size_type capacity() const { return container().capacity(); } @@ -267,8 +149,8 @@ public: void push_back (raw_value_type const ©) { insert (end(), copy); } - element_proxy pop_back () { - element_proxy result = at (end() - 1); + value_type pop_back () { + value_type result = at (end() - 1); erase (end() - 1); return result; } @@ -378,7 +260,7 @@ container_proxy } template -typename container_proxy::element_proxy +typename container_proxy::reference container_proxy ::at (size_type index) { @@ -389,11 +271,11 @@ container_proxy entry.reset (new shared_proxy (this, index)); } - return element_proxy (entry); + return reference (entry); } template -typename container_proxy::const_element_proxy +typename container_proxy::const_reference container_proxy ::at (size_type index) const { @@ -403,7 +285,7 @@ container_proxy container_proxy *mutable_this = const_cast(this); - return const_element_proxy (mutable_this->at (index)); + return const_reference (mutable_this->at (index)); } template diff --git a/include/boost/python/suite/indexing/container_suite.hpp b/include/boost/python/suite/indexing/container_suite.hpp index c8a301a2..36454c26 100755 --- a/include/boost/python/suite/indexing/container_suite.hpp +++ b/include/boost/python/suite/indexing/container_suite.hpp @@ -35,12 +35,14 @@ namespace indexing { template > struct container_suite { + typedef Algorithms algorithms; + typedef boost::python::return_value_policy default_policies; static visitor generate () { - return visitor (default_policies()); + return visitor (); } template diff --git a/include/boost/python/suite/indexing/container_traits.hpp b/include/boost/python/suite/indexing/container_traits.hpp index 5c121d96..6a570250 100755 --- a/include/boost/python/suite/indexing/container_traits.hpp +++ b/include/boost/python/suite/indexing/container_traits.hpp @@ -2,6 +2,9 @@ // // Header file container_traits.hpp // +// Traits information about entire containers for use in determining +// what Python methods to support for a container. +// // Copyright (c) 2003 Raoul M. Gough // // This material is provided "as is", with absolutely no warranty expressed @@ -26,75 +29,77 @@ #define container_traits_rmg_20030823_included #include "suite_utils.hpp" -#include "iterator_suite.hpp" +#include "iterator_traits.hpp" +#include "value_traits.hpp" #include namespace indexing { + ///////////////////////////////////////////////////////////////////////// + // Lowest common denominator traits - applicable to real containers + // and iterator pairs + ///////////////////////////////////////////////////////////////////////// + + template + struct base_container_traits + : public iterator_detail::traits_by_category + ::type + { + protected: + typedef typename + iterator_detail::traits_by_category + ::type base_type; + + BOOST_STATIC_CONSTANT (bool, is_mutable + = ! boost::is_const::value); + + public: + typedef Container container; + + typedef typename container::size_type size_type; + typedef typename container::size_type index_type; // at(), operator[] + typedef typename base_type::value_type key_type; // find, count, ... + + // *FIXME* should probably override the typedefs for iterator, + // value_type and reference with the const versions if !is_mutable + + typedef value_traits value_traits_; + + BOOST_STATIC_CONSTANT (bool, has_mutable_ref + = base_type::has_mutable_ref && is_mutable); + + BOOST_STATIC_CONSTANT (bool, has_find + = value_traits_::equality_comparable); + + // Assume the worst for everything else + BOOST_STATIC_CONSTANT (bool, has_insert = false); + BOOST_STATIC_CONSTANT (bool, has_erase = false); + BOOST_STATIC_CONSTANT (bool, has_pop_back = false); + BOOST_STATIC_CONSTANT (bool, has_push_back = false); + + // Forward visitor_helper to value_traits_ + template + static void visitor_helper (PythonClass &, Policy const &); + }; + ///////////////////////////////////////////////////////////////////////// // Traits for the iterator_pair container emulator ///////////////////////////////////////////////////////////////////////// template - struct iterator_pair_traits - : public - iterator_detail::traits_by_category::type + struct iterator_pair_traits : public base_container_traits { - 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; }; ///////////////////////////////////////////////////////////////////////// - // Lowest common denominator (almost all "real" containers would - // meet at least these requirements) + // Default container traits - almost all "real" containers would meet + // at least these requirements ///////////////////////////////////////////////////////////////////////// template - struct default_container_traits + struct default_container_traits : public base_container_traits { - protected: - static bool const is_mutable = ! boost::is_const::value; - - public: - typedef Container container; - - // *FIXME* should use value_type const and const_reference if !is_mutable - 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 IndexStyle const index_style - = ::indexing::iterator_traits::index_style; - - 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; + BOOST_STATIC_CONSTANT (bool, has_insert = is_mutable); + BOOST_STATIC_CONSTANT (bool, has_erase = is_mutable); }; ///////////////////////////////////////////////////////////////////////// @@ -104,8 +109,25 @@ namespace indexing { 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; + BOOST_STATIC_CONSTANT (bool, has_pop_back = is_mutable); + BOOST_STATIC_CONSTANT (bool, has_push_back = is_mutable); + }; + + ///////////////////////////////////////////////////////////////////////// + // Sequences within a container_proxy + ///////////////////////////////////////////////////////////////////////// + + template + struct container_proxy_traits : public default_sequence_traits + { + typedef Container container; + typedef typename container::raw_value_type value_type; // insert, ... + typedef typename container::raw_value_type key_type; // find, count, ... + typedef typename container::reference reference; // return values + + typedef value_traits value_traits_; + // Get value_traits for the reference type (i.e. element_proxy) + // to get the custom visitor_helper }; ///////////////////////////////////////////////////////////////////////// @@ -115,7 +137,8 @@ namespace indexing { template struct set_traits : public default_container_traits { - static IndexStyle const index_style = index_style_nonlinear; + BOOST_STATIC_CONSTANT (IndexStyle, index_style = index_style_nonlinear); + BOOST_STATIC_CONSTANT (bool, has_find = true); }; ///////////////////////////////////////////////////////////////////////// @@ -131,8 +154,22 @@ namespace indexing { typedef typename Container::key_type index_type; // at() typedef typename Container::key_type key_type; // find, count, ... - static IndexStyle const index_style = index_style_nonlinear; + BOOST_STATIC_CONSTANT (IndexStyle, index_style = index_style_nonlinear); + BOOST_STATIC_CONSTANT (bool, has_find = true); }; } +///////////////////////////////////////////////////////////////////////// +// Visitor helper function (foward to value_traits_ version) +///////////////////////////////////////////////////////////////////////// + +template +template +void +indexing::base_container_traits +::visitor_helper (PythonClass &pyClass, Policy const &policy) +{ + value_traits_::visitor_helper (pyClass, policy); +} + #endif // container_suite_rmg_20030823_included diff --git a/include/boost/python/suite/indexing/indexing.cpp b/include/boost/python/suite/indexing/indexing.cpp index b7582e20..1d27beaf 100755 --- a/include/boost/python/suite/indexing/indexing.cpp +++ b/include/boost/python/suite/indexing/indexing.cpp @@ -21,15 +21,27 @@ // $Id$ // +#ifndef NEWSTYLE +# define NEWSTYLE 1 +#endif + #include "container_proxy.hpp" #include "IntWrapper.hpp" +#if NEWSTYLE +#include "container_suite.hpp" +#endif + #include #include #include #include + +#if !NEWSTYLE #include #include +#endif + #include #include @@ -57,27 +69,47 @@ void pointer_increment (boost::shared_ptr const &ptr) (*ptr).increment(); } -IntWrapper *get_pointer (container_proxy >::value_type const &proxy) -{ - return &(*proxy); -} - BOOST_PYTHON_MODULE (indexing) { - boost::python::def ("trace", &IntWrapper::setTrace); + boost::python::def ("setTrace", &IntWrapper::setTrace); typedef std::vector Container; typedef container_proxy ProxyContainer; typedef boost::shared_ptr Pointer; typedef std::vector PointerContainer; - using boost::python::vector_indexing_suite; + boost::python::implicitly_convertible (); // typedef vector_indexing_suite Suite; +#if NEWSTYLE + typedef boost::python::return_value_policy + default_policies; + + // Not really the same thing - returning internal references + typedef indexing::visitor + ::algorithms + , boost::python::return_internal_reference<> > ProxySuite; + + typedef indexing::visitor + ::algorithms + , default_policies> ProxyContainerSuite; + + typedef indexing::visitor + ::algorithms + , default_policies> PointerContainerSuite; + +#else + using boost::python::vector_indexing_suite; + typedef vector_indexing_suite ProxySuite; typedef vector_indexing_suite ProxyContainerSuite; typedef vector_indexing_suite PointerContainerSuite; + boost::python::implicitly_convertible (); + + boost::python::register_ptr_to_python(); +#endif + boost::python::class_ ("Vector") .def (ProxySuite()) .def ("reserve", &Container::reserve); @@ -90,21 +122,11 @@ BOOST_PYTHON_MODULE (indexing) .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) diff --git a/include/boost/python/suite/indexing/slice.cpp b/include/boost/python/suite/indexing/slice.cpp index 3247860b..823b1d71 100755 --- a/include/boost/python/suite/indexing/slice.cpp +++ b/include/boost/python/suite/indexing/slice.cpp @@ -37,28 +37,6 @@ void indexing::slice::validate () const } } -///////////////////////////////////////////////////////////////////////////// -// Slice constructor -///////////////////////////////////////////////////////////////////////////// - -indexing::slice::slice (boost::python::detail::borrowed_reference ref) - : boost::python::object (ref) - , mStart (0) - , mStep (0) - , mStop (0) - , mDirection (0) -{ - if (!PySlice_Check (this->ptr())) - { - PyErr_SetString (PyExc_TypeError - , "slice constructor: passed a non-slice object"); - - boost::python::throw_error_already_set(); - } - - // This slice object is useless until setLength is called -} - ///////////////////////////////////////////////////////////////////////////// // Set up our member variables for a sequence of a given length ///////////////////////////////////////////////////////////////////////////// diff --git a/include/boost/python/suite/indexing/slice.hpp b/include/boost/python/suite/indexing/slice.hpp index 3c626a01..40a25bbb 100755 --- a/include/boost/python/suite/indexing/slice.hpp +++ b/include/boost/python/suite/indexing/slice.hpp @@ -24,7 +24,6 @@ #ifndef slice_rmg_20030910_included #define slice_rmg_20030910_included -#include #include #include @@ -38,7 +37,8 @@ namespace indexing // A newly constructed slice object is useless until setLength is called // - slice (boost::python::detail::borrowed_reference ref); + template slice (T const &ref); + void setLength (int sequenceLength); int start() const { validate(); return mStart; } @@ -59,6 +59,25 @@ namespace indexing }; } +template +indexing::slice::slice (T const &ref) + : boost::python::object (ref) + , mStart (0) + , mStep (0) + , mStop (0) + , mDirection (0) +{ + if (!PySlice_Check (this->ptr())) + { + PyErr_SetString (PyExc_TypeError + , "slice constructor: passed a non-slice object"); + + boost::python::throw_error_already_set(); + } + + // This slice object is still useless until setLength is called +} + namespace boost { namespace python { namespace converter { // Specialized converter to handle PySlice_Type objects template<> diff --git a/include/boost/python/suite/indexing/slice_handler.hpp b/include/boost/python/suite/indexing/slice_handler.hpp index 62a2e80d..afe849d5 100755 --- a/include/boost/python/suite/indexing/slice_handler.hpp +++ b/include/boost/python/suite/indexing/slice_handler.hpp @@ -40,6 +40,8 @@ namespace indexing { static boost::python::object make_getitem (Policy const &); static boost::python::object make_setitem (Policy const &); + static boost::python::object make_delitem (Policy const &); + static boost::python::object make_extend (Policy const &); private: typedef typename Algorithms::container container; @@ -47,6 +49,8 @@ namespace indexing static boost::python::list get_slice (container &, slice); static void set_slice (container &, slice, boost::python::object); + static void del_slice (container &, slice); + static void extend (container &, boost::python::object); struct postcall_override { @@ -111,7 +115,7 @@ namespace indexing , typename Algorithms::index_param from , typename Algorithms::index_param to) { - Algorithms::erase (c, from, to); + Algorithms::erase_range (c, from, to); } }; } @@ -183,6 +187,32 @@ indexing::slice_handler return boost::python::make_function (set_slice, policy); } +///////////////////////////////////////////////////////////////////////////// +// Return a function object that implements the slice version of __delitem__ +///////////////////////////////////////////////////////////////////////////// + +template +boost::python::object +indexing::slice_handler +::make_delitem (Policy const &policy) +{ + // should we try to get funky with policy::precall? + return boost::python::make_function (del_slice, policy); +} + +///////////////////////////////////////////////////////////////////////////// +// Return a function object that implements extend +///////////////////////////////////////////////////////////////////////////// + +template +boost::python::object +indexing::slice_handler +::make_extend (Policy const &policy) +{ + // should we try to get funky with policy::precall? + return boost::python::make_function (extend, policy); +} + ///////////////////////////////////////////////////////////////////////////// // Implementation for the slice version of __getitem__ ///////////////////////////////////////////////////////////////////////////// @@ -233,7 +263,12 @@ indexing::slice_handler } typedef typename Algorithms::container_traits traits; - typedef boost::python::extract extractor; + + // Try two kinds of extractors - the first is more efficient (using + // a reference to existing object, if possible and sensible) and the + // second allowing implicit conversions. + typedef boost::python::extract extractor1; + typedef boost::python::extract extractor2; // Note: any error during this operation will probably leave the // container partially updated. This can occur (for example) if the @@ -250,7 +285,17 @@ indexing::slice_handler { if (sl.inRange (index)) { - Algorithms::assign (c, index, extractor (iterPtr->current())); + extractor1 ex1 (iterPtr->current()); + + if (ex1.check()) + { + Algorithms::assign (c, index, ex1); + } + + else + { + Algorithms::assign (c, index, extractor2 (iterPtr->current())); + } } else if (sl.step() != 1) @@ -266,9 +311,22 @@ indexing::slice_handler // Could optimize this in some cases (i.e. if the length of // the replacement sequence is known) - maybe_insert - ::template apply (c, index - , extractor (iterPtr->current())); + extractor1 ex1 (iterPtr->current()); + + if (ex1.check()) + { + maybe_insert + ::template apply (c, index, ex1); + + Algorithms::assign (c, index, ex1); + } + + else + { + maybe_insert + ::template apply + (c, index, extractor2 (iterPtr->current())); + } } index += sl.step(); @@ -293,4 +351,41 @@ indexing::slice_handler } } +///////////////////////////////////////////////////////////////////////////// +// Implementation for the slice version of __delitem__ +///////////////////////////////////////////////////////////////////////////// + +template +void +indexing::slice_handler +::del_slice (container &c, slice sl) +{ + sl.setLength (Algorithms::size (c)); // Current length of our container + + Algorithms::erase_range (c, sl.start(), sl.stop()); +} + +///////////////////////////////////////////////////////////////////////////// +// Implementation of extend +///////////////////////////////////////////////////////////////////////////// + +template +void +indexing::slice_handler +::extend (container &c, boost::python::object values) +{ + boost::python::object length + ((boost::python::detail::new_reference + (PyInt_FromLong (Algorithms::size (c))))); + + indexing::slice sl + ((boost::python::detail::new_reference + (PySlice_New + (length.ptr() + , boost::python::object().ptr() + , boost::python::object().ptr())))); + + set_slice (c, sl, values); +} + #endif // slice_handler_rmg_20030909_included diff --git a/include/boost/python/suite/indexing/testarray.py b/include/boost/python/suite/indexing/testarray.py index ca85654f..c396371f 100755 --- a/include/boost/python/suite/indexing/testarray.py +++ b/include/boost/python/suite/indexing/testarray.py @@ -22,14 +22,43 @@ # $Id$ # -from testsuite import Array, getArray +from testsuite import Array, getArray, IntWrapper, setTrace + +setTrace (0) a = getArray() -print a[0], a[1], a[2], [x for x in a], a[0:-2], a[-1:-3:-1], a[0:54:0] +print a[0], a[1], a[2], [x for x in a], a[0:-2], a[-1:-3:-1] + +try: + a[0:54:0] + +except ValueError, e: + print "Got expected ValueError:", e + pass a[1] = 4 print a[0], a[1], a[2], [x for x in a], a[0:-2], a[-1:-3:-1] -print a[0:43] +print a[0:43] # Try index beyond length + +# Try slice assignment with correct length +a[0:2] = [IntWrapper(-1), IntWrapper(-2)] +print [x for x in a] + +# Try slice assignment with overlong sequence +try: + a[0:1] = [IntWrapper(-1), IntWrapper(-2)] + +except TypeError, e: + print "Got expected TypeError:", e + pass + +# Try slice assignment with short sequence +try: + a[0:3] = [IntWrapper(-1), IntWrapper(-2)] + +except TypeError, e: + print "Got expected TypeError:", e + pass diff --git a/include/boost/python/suite/indexing/testindexing.py b/include/boost/python/suite/indexing/testindexing.py index c7b686ea..1ce87ec3 100755 --- a/include/boost/python/suite/indexing/testindexing.py +++ b/include/boost/python/suite/indexing/testindexing.py @@ -23,48 +23,89 @@ # from indexing import * +import sys -trace(0) +setTrace(0) -def test_from_empty(v): +if hasattr(sys, "gettotalrefcount"): + def dumper() : print "refcount:", sys.gettotalrefcount() + + maybe_dump_refcount = dumper +else: + def dumper() : pass + + maybe_dump_refcount = dumper + +def test_from_empty(v, outfile = sys.stdout): # v.reserve (10); - print v + print >> outfile, v v.append (IntWrapper (1)) v.append (IntWrapper (2)) v.append (IntWrapper (3)) - print v[0], v[1], v[2] + print >> outfile, v[0], v[1], v[2] try: - print v[0], v[1:][0], v[-1:][0] + print >> outfile, v[0], v[1:][0], v[-1:][0] except Exception, e: - print e + print >> outfile, e pass copy = v[1] - print "copy is %s, v[1] is %s" % (copy, v[1]) + print >> outfile, "copy is %s, v[1] is %s" % (copy, v[1]) v[1].increment() - print "copy is %s, v[1] is %s" % (copy, v[1]) + print >> outfile, "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]) + print >> outfile, "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]) + print >> outfile, "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]) + print >> outfile, "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]) + print >> outfile, "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 >> outfile, "slice[0] is %s, copy is %s, v[1] is %s" % (slice[0], copy, v[1]) - print "=============" +# if hasattr(sys, "gettotalrefcount"): +# for x in range(20): +# v[:] = [1,2,3,4] +# maybe_dump_refcount() + +# for x in range(9): +# v[:] = [1,2,3,4] +# maybe_dump_refcount() + + print >> outfile, "=============" + +#test_from_empty (Vector_proxy()) + +class write_eater: + def write (self, str): + pass + +# Run everything through once to settle reference counters +test_from_empty (Vector(), write_eater()) +test_from_empty (ProxyVector(), write_eater()) +test_from_empty (PointerVector(), write_eater()) +test_from_empty ([], write_eater()) + +# Run again with output (and ref-count dumping, if possible) +maybe_dump_refcount() test_from_empty (Vector()) +maybe_dump_refcount() + test_from_empty (ProxyVector()) +maybe_dump_refcount() + test_from_empty (PointerVector()) +maybe_dump_refcount() + test_from_empty ([]) +maybe_dump_refcount() diff --git a/include/boost/python/suite/indexing/testsuite.cpp b/include/boost/python/suite/indexing/testsuite.cpp index 2f11aa2d..3fe6ce10 100755 --- a/include/boost/python/suite/indexing/testsuite.cpp +++ b/include/boost/python/suite/indexing/testsuite.cpp @@ -36,6 +36,7 @@ #include #include "iterator_pair.hpp" +#include "container_proxy.hpp" indexing::iterator_pair getArray() { @@ -63,7 +64,8 @@ BOOST_PYTHON_MODULE(testsuite) boost::python::class_ ("IntWrapper", boost::python::init()) .def ("increment", &IntWrapper::increment) - .def ("__repr__", repr); + .def ("__repr__", repr) + .def ("__cmp__", compare) ; typedef std::vector Container1; @@ -96,4 +98,9 @@ BOOST_PYTHON_MODULE(testsuite) // deletes! boost::python::class_("Vector_ref") .def (indexing::container_suite::generate (boost::python::return_internal_reference<>())); + + typedef container_proxy > Container6; + + boost::python::class_("Vector_proxy") + .def (indexing::container_suite::generate ()); } diff --git a/include/boost/python/suite/indexing/testvector.py b/include/boost/python/suite/indexing/testvector.py index 4e4b161f..72f17a22 100755 --- a/include/boost/python/suite/indexing/testvector.py +++ b/include/boost/python/suite/indexing/testvector.py @@ -22,18 +22,60 @@ # $Id$ # -from testsuite import Vector +from testsuite import Vector, Vector_ref, setTrace -v = Vector() +def test_vector(v): + v.append(1) + v.append(2) + v.append(3) -v.append(1) -v.append(2) -v.append(3) + print v[0], v[1], v[2], [x for x in v] -print v[0], v[1], v[2], [x for x in v] + v[1] = 4 -v[1] = 4 + print v[0], v[1], v[2], [x for x in v], v[0:2] -print v[0], v[1], v[2], [x for x in v] + # Try slice assignment with equal lengths + v[0:2] = [9, 8] + print [x for x in v] -print v[0:2] + # Try slice assignment with shorter replacement + v[0:2] = [7] + print [x for x in v] + + # Try slice assignment with longer replacement + v[0:1] = [6, 5, 4] + print [x for x in v] + + # Try element deletion + del v[0] + print [x for x in v] + + # Try slice deletion + del v[0:2] + print [x for x in v] + + try: + del v[3] + + except IndexError, e: + print "Got expected exception:", e + + print [x for x in v] + + del v[0:999] + print [x for x in v] + + v.append (3) + print v[0] + del v[:] + print [x for x in v] + +print "Plain vector:" +print "=============" +test_vector (Vector()) + +print "Ref vector:" +print "===========" +setTrace(0) +test_vector (Vector_ref()) diff --git a/include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp b/include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp index 086d000b..e44d34a8 100755 --- a/include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp +++ b/include/boost/python/suite/indexing/vector_indexing_suite_ext.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "container_proxy.hpp" +#include "container_suite.hpp" using namespace boost::python; @@ -23,11 +23,6 @@ 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") @@ -35,6 +30,7 @@ BOOST_PYTHON_MODULE(vector_indexing_suite_ext) .def(init()) .def(init()) .def("__repr__", &X::repr) + .def("__eq__", &X::operator==) .def("reset", &X::reset) .def("foo", &X::foo) ; @@ -45,12 +41,8 @@ BOOST_PYTHON_MODULE(vector_indexing_suite_ext) typedef std::vector RawContainer; typedef container_proxy Container; - boost::python::register_ptr_to_python(); - - implicitly_convertible(); - class_("XVec") - .def(vector_indexing_suite()) + .def(indexing::container_suite::generate()) ; // Compile check only... diff --git a/include/boost/python/suite/indexing/visitor.hpp b/include/boost/python/suite/indexing/visitor.hpp index d759cecd..79ca0a8b 100755 --- a/include/boost/python/suite/indexing/visitor.hpp +++ b/include/boost/python/suite/indexing/visitor.hpp @@ -28,14 +28,68 @@ #include #include +#include +#include namespace indexing { + namespace detail { + template + struct precall_only : public boost::python::default_call_policies + { + // This policies struct uses default policies for everything + // except precall, which must be provided by the template + // argument. + + precall_only () : mPrecall () { } + explicit precall_only (PrecallPolicy const ©) : mPrecall (copy) { } + + bool precall (PyObject *args) { return mPrecall.precall (args); } + bool precall (PyObject *args) const { return mPrecall.precall (args); } + + private: + PrecallPolicy mPrecall; + }; + } + + ////////////////////////////////////////////////////////////////////////// + // __len__ dummy + ////////////////////////////////////////////////////////////////////////// + + template + struct maybe_add_len { + template + static void apply (PythonClass &, Algorithms const &, Policy const &) { } + }; + + ////////////////////////////////////////////////////////////////////////// + // __len__ real + ////////////////////////////////////////////////////////////////////////// + + template<> + struct maybe_add_len { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + pyClass.def ("__len__", &Algorithms::size, policy); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // __getitem__ dummy + ////////////////////////////////////////////////////////////////////////// + template struct maybe_add_getitem { template static void apply (PythonClass &, Algorithms const &, Policy const &) { } }; + ////////////////////////////////////////////////////////////////////////// + // __getitem__ no-slice + ////////////////////////////////////////////////////////////////////////// + template<> struct maybe_add_getitem { template @@ -47,6 +101,10 @@ namespace indexing { } }; + ////////////////////////////////////////////////////////////////////////// + // __getitem__ with slice + ////////////////////////////////////////////////////////////////////////// + template<> struct maybe_add_getitem { template @@ -60,12 +118,20 @@ namespace indexing { } }; + ////////////////////////////////////////////////////////////////////////// + // __setitem__ dummy + ////////////////////////////////////////////////////////////////////////// + template struct maybe_add_setitem { template static void apply (PythonClass &, Algorithms const &, Policy const &) { } }; + ////////////////////////////////////////////////////////////////////////// + // __setitem__ no-slice + ////////////////////////////////////////////////////////////////////////// + template<> struct maybe_add_setitem { template @@ -77,6 +143,10 @@ namespace indexing { } }; + ////////////////////////////////////////////////////////////////////////// + // __setitem__ with slice + ////////////////////////////////////////////////////////////////////////// + template<> struct maybe_add_setitem { template @@ -90,12 +160,62 @@ namespace indexing { } }; + ////////////////////////////////////////////////////////////////////////// + // __delitem__ dummy + ////////////////////////////////////////////////////////////////////////// + + template + struct maybe_add_delitem { + template + static void apply (PythonClass &, Algorithms const &, Policy const &) { } + }; + + ////////////////////////////////////////////////////////////////////////// + // __delitem__ no-slice + ////////////////////////////////////////////////////////////////////////// + + template<> + struct maybe_add_delitem { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + pyClass.def ("__delitem__", &Algorithms::erase_one, policy); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // __delitem__ with slice + ////////////////////////////////////////////////////////////////////////// + + template<> + struct maybe_add_delitem { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + pyClass.def ("__delitem__", &Algorithms::erase_one, policy); + pyClass.def ("__delitem__" + , slice_handler::make_delitem (policy)); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // __iter__ dummy + ////////////////////////////////////////////////////////////////////////// + template struct maybe_add_iter { template static void apply (PythonClass &, Algorithms const &, Policy const &) { } }; + ////////////////////////////////////////////////////////////////////////// + // __iter__ real + ////////////////////////////////////////////////////////////////////////// + template<> struct maybe_add_iter { template @@ -103,18 +223,29 @@ namespace indexing { , Algorithms const & , Policy const &policy) { + // *FIXME* seperate precall and postcall portions of the + // policy (precall when generating the range object, postcall + // when returing from range.next()) pyClass.def ("__iter__" , boost::python::range (Algorithms::begin , Algorithms::end)); } }; + ////////////////////////////////////////////////////////////////////////// + // append dummy + ////////////////////////////////////////////////////////////////////////// + template struct maybe_add_append { template static void apply (PythonClass &, Algorithms const &, Policy const &) { } }; + ////////////////////////////////////////////////////////////////////////// + // append real + ////////////////////////////////////////////////////////////////////////// + template<> struct maybe_add_append { template @@ -126,6 +257,155 @@ namespace indexing { } }; + ////////////////////////////////////////////////////////////////////////// + // extend dummy + ////////////////////////////////////////////////////////////////////////// + + template + struct maybe_add_extend { + template + static void apply (PythonClass &, Algorithms const &, Policy const &) { } + }; + + ////////////////////////////////////////////////////////////////////////// + // extend real + ////////////////////////////////////////////////////////////////////////// + + template<> + struct maybe_add_extend { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + pyClass.def ("extend" + , slice_handler::make_extend (policy)); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // index dummy + ////////////////////////////////////////////////////////////////////////// + + template + struct maybe_add_index { + template + static void apply (PythonClass &, Algorithms const &, Policy const &) { } + }; + + ////////////////////////////////////////////////////////////////////////// + // index real + ////////////////////////////////////////////////////////////////////////// + + namespace detail { + template + typename Algorithms::index_type + get_index (typename Algorithms::container &c + , typename Algorithms::key_param key) + { + typename Algorithms::iterator temp (Algorithms::find (c, key)); + + if (temp == Algorithms::end(c)) + { + PyErr_SetString (PyExc_ValueError + , "get_index: element not found"); + + boost::python::throw_error_already_set (); + } + + return std::distance (Algorithms::begin (c), temp); + } + } + + template<> + struct maybe_add_index { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + pyClass.def ("index", detail::get_index, policy); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // count dummy + ////////////////////////////////////////////////////////////////////////// + + template + struct maybe_add_count { + template + static void apply (PythonClass &, Algorithms const &, Policy const &) { } + }; + + ////////////////////////////////////////////////////////////////////////// + // count real (sequences without indexing) + ////////////////////////////////////////////////////////////////////////// + + template<> + struct maybe_add_count { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + pyClass.def ("count", Algorithms::count, policy); + } + }; + + + ////////////////////////////////////////////////////////////////////////// + // count real (sequences with indexing) + ////////////////////////////////////////////////////////////////////////// + + template<> + struct maybe_add_count { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + // This is identical to the index_style_none version. Doing it + // this way avoids using a partial specialization for + // + pyClass.def ("count", Algorithms::count, policy); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // count real (associative containers). add has_key + ////////////////////////////////////////////////////////////////////////// + + namespace detail { + template + bool has_key (typename Algorithms::container &c + , typename Algorithms::key_param key) + { + return Algorithms::find (c, key) != Algorithms::end (c); + } + } + + template<> + struct maybe_add_count { + template + static void apply (PythonClass &pyClass + , Algorithms const & + , Policy const &policy) + { + // Nearest equivalent is has_key, since Python dictionaries + // have at most one value for a key. + pyClass.def ("has_key", detail::has_key, policy); + + // Maybe this makes sense for multimap or multiset. Then again, + // maybe they should always return a list of elements for a key? + pyClass.def ("count", Algorithms::count, policy); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // Do-all visitor + ////////////////////////////////////////////////////////////////////////// + template class visitor : public boost::python::def_visitor< visitor< Algorithms, Policy > > @@ -136,23 +416,49 @@ namespace indexing { typedef Algorithms algorithms; typedef typename algorithms::container_traits traits; - visitor (Policy const &policy) : mPolicy (policy) { } + explicit visitor (Policy const &policy = Policy()) : mPolicy (policy) { } template void visit (PythonClass &pyClass) const { + detail::precall_only precallPolicy (mPolicy); + + // Note - this will add __len__ for anything that can determine + // its size, even if that might be inefficient (e.g. have linear + // time complexity). It might be better to add a new feature + // selection flag to the container_traits to make this + // configurable. + maybe_add_len + ::apply (pyClass, algorithms(), precallPolicy); + maybe_add_getitem ::apply (pyClass, algorithms(), mPolicy); maybe_add_setitem ::apply (pyClass, algorithms(), mPolicy); + maybe_add_delitem + ::apply (pyClass, algorithms(), mPolicy); + maybe_add_iter<((traits::index_style != index_style_linear) && traits::has_copyable_iter)> ::apply (pyClass, algorithms(), mPolicy); maybe_add_append - ::apply (pyClass, algorithms(), mPolicy); + ::apply (pyClass, algorithms(), precallPolicy); + + maybe_add_extend<(traits::has_insert + && traits::index_style == index_style_linear)> + ::apply (pyClass, algorithms(), precallPolicy); + + maybe_add_index<(traits::has_find + && (traits::index_style == index_style_linear))> + ::apply (pyClass, algorithms(), precallPolicy); + + maybe_add_count + ::apply (pyClass, algorithms(), precallPolicy); + + Algorithms::visitor_helper (pyClass, mPolicy); } }; }