From 2721c3c97e85245adc22b76209ce244b1ebcf7ea Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Tue, 13 Jul 2004 17:23:53 +0000 Subject: [PATCH] Repair postfix increment proxies for input iterators [SVN r23508] --- include/boost/iterator/iterator_facade.hpp | 71 ++++++++++++++----- include/boost/iterator/new_iterator_tests.hpp | 34 +++++++-- test/iterator_facade.cpp | 12 ++-- 3 files changed, 92 insertions(+), 25 deletions(-) diff --git a/include/boost/iterator/iterator_facade.hpp b/include/boost/iterator/iterator_facade.hpp index c7da1d7..10e8116 100644 --- a/include/boost/iterator/iterator_facade.hpp +++ b/include/boost/iterator/iterator_facade.hpp @@ -26,15 +26,13 @@ #include #include +#include #include #include #include #include #include - -#if BOOST_WORKAROUND(BOOST_MSVC, == 1200) -# include -#endif +#include #include // this goes last @@ -125,10 +123,6 @@ namespace boost // value must be read and stored away before the increment occurs // so that *a++ yields the originally referenced element and not // the next one. - // - // In general, we can't determine that such an iterator isn't - // writable -- we also need to store a copy of the old iterator so - // that it can be written into. template class postfix_increment_proxy { @@ -136,6 +130,32 @@ namespace boost public: explicit postfix_increment_proxy(Iterator const& x) : stored_value(*x) + {} + + // Returning a mutable reference allows nonsense like + // (*r++).mutate(), but it imposes fewer assumptions about the + // behavior of the value_type. In particular, recall taht + // (*r).mutate() is legal if operator* returns by value. + value_type& + operator*() const + { + return this->stored_value; + } + private: + mutable value_type stored_value; + }; + + // + // In general, we can't determine that such an iterator isn't + // writable -- we also need to store a copy of the old iterator so + // that it can be written into. + template + class writable_postfix_increment_proxy + { + typedef typename iterator_value::type value_type; + public: + explicit writable_postfix_increment_proxy(Iterator const& x) + : stored_value(*x) , stored_iterator(x) {} @@ -143,14 +163,14 @@ namespace boost // value_type(*r++) can work. In this case, *r is the same as // *r++, and the conversion operator below is used to ensure // readability. - postfix_increment_proxy const& + writable_postfix_increment_proxy const& operator*() const { return *this; } // Provides readability of *r++ - operator value_type const&() const + operator value_type&() const { return stored_value; } @@ -171,16 +191,31 @@ namespace boost return x; } private: - value_type stored_value; + mutable value_type stored_value; Iterator stored_iterator; }; + // A metafunction to choose the result type of postfix ++ + // + // Because the C++98 input iterator requirements say that *r++ has + // type T (value_type), implementations of some standard + // algorithms like lexicographical_compare may use constructions + // like: + // + // *r++ < *s++ + // + // If *r++ returns a proxy (as required if r is writable but not + // multipass), this sort of expression will fail unless the proxy + // supports the operator<. Since there are any number of such + // operations, we're not going to try to support them. Therefore, + // even if r++ returns a proxy, *r++ will only return a proxy if + // CategoryOrTraversal is convertible to std::output_iterator_tag. template struct postfix_increment_result - : mpl::if_< + : mpl::apply_if< mpl::and_< - // This is only needed for readable iterators - is_convertible + // A proxy is only needed for readable iterators + is_convertible // No multipass iterator can have values that disappear // before positions can be re-visited @@ -191,8 +226,12 @@ namespace boost > > > - , postfix_increment_proxy - , Iterator + , mpl::if_< + is_convertible + , writable_postfix_increment_proxy + , postfix_increment_proxy + > + , mpl::identity > {}; diff --git a/include/boost/iterator/new_iterator_tests.hpp b/include/boost/iterator/new_iterator_tests.hpp index e2abd17..9424b36 100644 --- a/include/boost/iterator/new_iterator_tests.hpp +++ b/include/boost/iterator/new_iterator_tests.hpp @@ -32,16 +32,32 @@ namespace boost { + +// Do separate tests for *i++ so we can treat, e.g., smart pointers, +// as readable and/or writable iterators. template -void readable_iterator_test_aux(Iterator i1, T v, mpl::true_) +void readable_iterator_traversal_test(Iterator i1, T v, mpl::true_) { - assert(v == *i1++); + T v2 = *i1++; + assert(v == v2); } template -void readable_iterator_test_aux(const Iterator i1, T v, mpl::false_) +void readable_iterator_traversal_test(const Iterator i1, T v, mpl::false_) {} +template +void writable_iterator_traversal_test(Iterator i1, T v, mpl::true_) +{ + ++i1; // we just wrote into that position + *i1++ = v; +} + +template +void writable_iterator_traversal_test(const Iterator i1, T v, mpl::false_) +{} + + // Preconditions: *i == v template void readable_iterator_test(const Iterator i1, T v) @@ -56,7 +72,7 @@ void readable_iterator_test(const Iterator i1, T v) assert(v2 == v); # if !BOOST_WORKAROUND(__MWERKS__, <= 0x2407) - readable_iterator_test_aux(i1, v, detail::is_postfix_incrementable()); + readable_iterator_traversal_test(i1, v, detail::is_postfix_incrementable()); // I think we don't really need this as it checks the same things as // the above code. @@ -65,10 +81,18 @@ void readable_iterator_test(const Iterator i1, T v) } template -void writable_iterator_test(Iterator i, T v) +void writable_iterator_test(Iterator i, T v, T v2) { Iterator i2(i); // Copy Constructible *i2 = v; + +# if !BOOST_WORKAROUND(__MWERKS__, <= 0x2407) + writable_iterator_traversal_test( + i, v2, mpl::and_< + detail::is_incrementable + , detail::is_postfix_incrementable + >()); +# endif } template diff --git a/test/iterator_facade.cpp b/test/iterator_facade.cpp index 1525505..c23707c 100755 --- a/test/iterator_facade.cpp +++ b/test/iterator_facade.cpp @@ -6,16 +6,17 @@ #include #include +#include // This is a really, really limited test so far. All we're doing // right now is checking that the postfix++ proxy for single-pass // iterators works properly. -template +template class counter_iterator : public boost::iterator_facade< - counter_iterator + counter_iterator , int const - , boost::single_pass_traversal_tag + , CategoryOrTraversal , Ref > { @@ -62,6 +63,9 @@ int main() boost::readable_iterator_test(counter_iterator(&state), 0); state = 3; boost::readable_iterator_test(counter_iterator(&state), 3); - boost::writable_iterator_test(counter_iterator(&state), 9); + state = 5; + boost::readable_iterator_test(counter_iterator(&state), 5); + boost::writable_iterator_test(counter_iterator(&state), 9, 7); + BOOST_ASSERT(state == 7); return 0; }