diff --git a/beast/container/detail/aged_container_iterator.h b/beast/container/detail/aged_container_iterator.h index 363447c416..206b4a424f 100644 --- a/beast/container/detail/aged_container_iterator.h +++ b/beast/container/detail/aged_container_iterator.h @@ -37,7 +37,7 @@ namespace detail { template < bool is_const, class Iterator, - class Base = + class Base = std::iterator < typename std::iterator_traits ::iterator_category, typename std::conditional - aged_container_iterator (aged_container_iterator < - false, OtherIterator, OtherBase> const& other) + // copy constructor + aged_container_iterator ( + aged_container_iterator + const& other) = default; + + // Disable constructing a const_iterator from a non-const_iterator. + // Converting between reverse and non-reverse iterators should be explicit. + template ::value == false>::type> + explicit aged_container_iterator (aged_container_iterator < + other_is_const, OtherIterator, OtherBase> const& other) : m_iter (other.m_iter) { } + // Disable constructing a const_iterator from a non-const_iterator. + template ::type> + aged_container_iterator (aged_container_iterator < + other_is_const, Iterator, OtherBase> const& other) + : m_iter (other.m_iter) + { + } + + // Disable assigning a const_iterator to a non-const iterator template - aged_container_iterator& operator= (aged_container_iterator < - other_is_const, OtherIterator, OtherBase> const& other) + auto + operator= (aged_container_iterator < + other_is_const, OtherIterator, OtherBase> const& other) -> + typename std::enable_if < + other_is_const == false || is_const == true, + aged_container_iterator&>::type { m_iter = other.m_iter; return *this; diff --git a/beast/container/detail/aged_ordered_container.h b/beast/container/detail/aged_ordered_container.h index c248252cc4..0f04921f64 100644 --- a/beast/container/detail/aged_ordered_container.h +++ b/beast/container/detail/aged_ordered_container.h @@ -42,6 +42,18 @@ namespace beast { namespace detail { +// Traits templates used to discern reverse_iterators, which are disallowed +// for mutating operations. +template +struct is_boost_reverse_iterator + : std::false_type +{}; + +template +struct is_boost_reverse_iterator> + : std::true_type +{}; + /** Associative container where each element is also indexed by time. This container mirrors the interface of the standard library ordered @@ -203,7 +215,7 @@ private: { return this->member() (k, extract (e.value)); } - + template bool operator() (element const& e, K const& k) const { @@ -215,7 +227,7 @@ private: { return this->member() (k, extract (e.value)); } - + bool operator() (element const& e, Key const& k) const { return this->member() (extract (e.value), k); @@ -384,13 +396,14 @@ private: return p; } - void delete_element (element* p) + void delete_element (element const* p) { ElementAllocatorTraits::destroy (m_config.alloc(), p); - ElementAllocatorTraits::deallocate (m_config.alloc(), p, 1); + ElementAllocatorTraits::deallocate ( + m_config.alloc(), const_cast(p), 1); } - void unlink_and_delete_element (element* p) + void unlink_and_delete_element (element const* p) { chronological.list.erase ( chronological.list.iterator_to (*p)); @@ -412,11 +425,13 @@ public: typedef typename std::allocator_traits < Allocator>::const_pointer const_pointer; - typedef detail::aged_container_iterator iterator; typedef detail::aged_container_iterator const_iterator; - typedef detail::aged_container_iterator reverse_iterator; typedef detail::aged_container_iterator const_reverse_iterator; @@ -433,11 +448,13 @@ public: class chronological_t { public: - typedef detail::aged_container_iterator iterator; typedef detail::aged_container_iterator const_iterator; - typedef detail::aged_container_iterator reverse_iterator; typedef detail::aged_container_iterator const_reverse_iterator; @@ -823,7 +840,7 @@ public: template typename std::enable_if ::type - insert (const_iterator const& /*hint*/, value_type const& value) + insert (const_iterator /*hint*/, value_type const& value) { // VFALCO TODO Figure out how to utilize 'hint' return insert (value); @@ -840,7 +857,7 @@ public: template typename std::enable_if ::type - insert (const_iterator const& /*hint*/, value_type&& value) + insert (const_iterator /*hint*/, value_type&& value) { // VFALCO TODO Figure out how to utilize 'hint' return insert (std::move (value)); @@ -882,7 +899,7 @@ public: template void - insert (InputIt first, InputIt const& last) + insert (InputIt first, InputIt last) { for (; first != last; ++first) insert (cend(), *first); @@ -911,7 +928,7 @@ public: // map, set template auto - emplace_hint (const_iterator const& hint, Args&&... args) -> + emplace_hint (const_iterator hint, Args&&... args) -> typename std::enable_if >::type; @@ -919,24 +936,26 @@ public: template typename std::enable_if ::type - emplace_hint (const_iterator const& /*hint*/, Args&&... args) + emplace_hint (const_iterator /*hint*/, Args&&... args) { // VFALCO TODO Figure out how to utilize 'hint' return emplace ( std::forward (args)...); } - template + // enable_if prevents erase (reverse_iterator pos) from compiling + template ::value>> detail::aged_container_iterator - erase (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos); + erase (detail::aged_container_iterator pos); - template + // enable_if prevents erase (reverse_iterator first, reverse_iterator last) + // from compiling + template ::value>> detail::aged_container_iterator - erase (detail::aged_container_iterator < - is_const, Iterator, Base> first, - detail::aged_container_iterator < - is_const, Iterator, Base> const& last); + erase (detail::aged_container_iterator first, + detail::aged_container_iterator last); template auto @@ -948,10 +967,11 @@ public: //-------------------------------------------------------------------------- - template + // enable_if prevents touch (reverse_iterator pos) from compiling + template ::value>> void - touch (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos) + touch (detail::aged_container_iterator pos) { touch (pos, clock().now()); } @@ -1047,7 +1067,7 @@ public: const_iterator upper_bound (K const& k) const { - return const_iterator (m_cont.upper_bound (k, + return const_iterator (m_cont.upper_bound (k, std::cref (m_config.key_compare()))); } @@ -1176,10 +1196,12 @@ public: } private: - template + // enable_if prevents erase (reverse_iterator pos, now) from compiling + template ::value>> void touch (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos, + is_const, Iterator, Base> pos, typename clock_type::time_point const& now); template auto aged_ordered_container :: -emplace_hint (const_iterator const& hint, Args&&... args) -> +emplace_hint (const_iterator hint, Args&&... args) -> typename std::enable_if >::type { @@ -1716,36 +1738,27 @@ emplace_hint (const_iterator const& hint, Args&&... args) -> template -template -auto +template +detail::aged_container_iterator aged_ordered_container :: -erase (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos) -> - detail::aged_container_iterator +erase (detail::aged_container_iterator pos) { - auto iter (pos.iterator()); - auto p (&*iter++); - unlink_and_delete_element (p); + unlink_and_delete_element(&*((pos++).iterator())); return detail::aged_container_iterator < - false, Iterator, Base> (iter); + false, Iterator, Base> (pos.iterator()); } template -template -auto +template +detail::aged_container_iterator aged_ordered_container :: -erase (detail::aged_container_iterator < - is_const, Iterator, Base> first, - detail::aged_container_iterator < - is_const, Iterator, Base> const& last) -> - detail::aged_container_iterator +erase (detail::aged_container_iterator first, + detail::aged_container_iterator last) { for (; first != last;) - { - auto p (&*first++); - unlink_and_delete_element (p); - } + unlink_and_delete_element(&*((first++).iterator())); + return detail::aged_container_iterator < false, Iterator, Base> (first.iterator()); } @@ -1839,11 +1852,11 @@ operator== ( template -template +template void aged_ordered_container :: touch (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos, + is_const, Iterator, Base> pos, typename clock_type::time_point const& now) { auto& e (*pos.iterator()); diff --git a/beast/container/detail/aged_unordered_container.h b/beast/container/detail/aged_unordered_container.h index deaf741b8c..c4bd8dfec7 100644 --- a/beast/container/detail/aged_unordered_container.h +++ b/beast/container/detail/aged_unordered_container.h @@ -186,7 +186,7 @@ private: { return this->member() (extract (e.value)); } - + Hash& hash_function() { return this->member(); @@ -222,7 +222,7 @@ private: { return this->member() (k, extract (e.value)); } - + template bool operator() (element const& e, K const& k) const { @@ -234,7 +234,7 @@ private: { return this->member() (k, extract (e.value)); } - + bool operator() (element const& e, Key const& k) const { return this->member() (extract (e.value), k); @@ -584,13 +584,14 @@ private: return p; } - void delete_element (element* p) + void delete_element (element const* p) { ElementAllocatorTraits::destroy (m_config.alloc(), p); - ElementAllocatorTraits::deallocate (m_config.alloc(), p, 1); + ElementAllocatorTraits::deallocate ( + m_config.alloc(), const_cast(p), 1); } - void unlink_and_delete_element (element* p) + void unlink_and_delete_element (element const* p) { chronological.list.erase ( chronological.list.iterator_to (*p)); @@ -609,12 +610,14 @@ public: typedef typename std::allocator_traits < Allocator>::const_pointer const_pointer; - typedef detail::aged_container_iterator iterator; typedef detail::aged_container_iterator const_iterator; - typedef detail::aged_container_iterator local_iterator; typedef detail::aged_container_iterator const_local_iterator; @@ -631,11 +634,13 @@ public: class chronological_t { public: - typedef detail::aged_container_iterator iterator; typedef detail::aged_container_iterator const_iterator; - typedef detail::aged_container_iterator reverse_iterator; typedef detail::aged_container_iterator const_reverse_iterator; @@ -1021,7 +1026,7 @@ public: template typename std::enable_if ::type - insert (const_iterator const& /*hint*/, value_type const& value) + insert (const_iterator /*hint*/, value_type const& value) { // VFALCO TODO The hint could be used to let // the client order equal ranges @@ -1043,7 +1048,7 @@ public: template typename std::enable_if ::type - insert (const_iterator const& /*hint*/, value_type&& value) + insert (const_iterator /*hint*/, value_type&& value) { // VFALCO TODO The hint could be used to let // the client order equal ranges @@ -1083,7 +1088,7 @@ public: } template - void insert (InputIt first, InputIt const& last) + void insert (InputIt first, InputIt last) { insert (first, last, typename std::iterator_traits < @@ -1113,7 +1118,7 @@ public: // set, map template auto - emplace_hint (const_iterator const& /*hint*/, Args&&... args) -> + emplace_hint (const_iterator /*hint*/, Args&&... args) -> typename std::enable_if >::type; @@ -1121,7 +1126,7 @@ public: template typename std::enable_if ::type - emplace_hint (const_iterator const& /*hint*/, Args&&... args) + emplace_hint (const_iterator /*hint*/, Args&&... args) { // VFALCO TODO The hint could be used for multi, to let // the client order equal ranges @@ -1132,14 +1137,14 @@ public: template detail::aged_container_iterator erase (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos); + is_const, Iterator, Base> pos); template detail::aged_container_iterator erase (detail::aged_container_iterator < is_const, Iterator, Base> first, detail::aged_container_iterator < - is_const, Iterator, Base> const& last); + is_const, Iterator, Base> last); template auto @@ -1152,7 +1157,7 @@ public: template void touch (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos) + is_const, Iterator, Base> pos) { touch (pos, clock().now()); } @@ -1349,24 +1354,11 @@ public: class OtherAllocator, bool maybe_multi = IsMulti > - typename std::enable_if ::type + typename std::enable_if ::type operator== ( aged_unordered_container const& other) const - { - if (size() != other.size()) - return false; - for (auto iter (cbegin()), last (cend()), olast (other.cend()); - iter != last; ++iter) - { - auto oiter (other.find (extract (*iter))); - if (oiter == olast) - return false; - } - return true; - } + OtherAllocator> const& other) const; template < bool OtherIsMap, @@ -1377,35 +1369,11 @@ public: class OtherAllocator, bool maybe_multi = IsMulti > - typename std::enable_if ::type + typename std::enable_if ::type operator== ( aged_unordered_container const& other) const - { - if (size() != other.size()) - return false; - typedef std::pair EqRng; - for (auto iter (cbegin()), last (cend()); iter != last;) - { - auto const& k (extract (*iter)); - auto const eq (equal_range (k)); - auto const oeq (other.equal_range (k)); -#if BEAST_NO_CXX14_IS_PERMUTATION - if (std::distance (eq.first, eq.second) != - std::distance (oeq.first, oeq.second) || - ! std::is_permutation (eq.first, eq.second, oeq.first)) - return false; -#else - if (! std::is_permutation (eq.first, - eq.second, oeq.first, oeq.second)) - return false; -#endif - iter = eq.second; - } - return true; - } + OtherAllocator> const& other) const; template < bool OtherIsMulti, @@ -1456,7 +1424,7 @@ private: template void - insert_unchecked (InputIt first, InputIt const& last) + insert_unchecked (InputIt first, InputIt last) { for (; first != last; ++first) insert_unchecked (*first); @@ -1464,7 +1432,7 @@ private: template void - insert (InputIt first, InputIt const& last, + insert (InputIt first, InputIt last, std::input_iterator_tag) { for (; first != last; ++first) @@ -1473,7 +1441,7 @@ private: template void - insert (InputIt first, InputIt const& last, + insert (InputIt first, InputIt last, std::random_access_iterator_tag) { auto const n (std::distance (first, last)); @@ -1484,7 +1452,7 @@ private: template void touch (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos, + is_const, Iterator, Base> pos, typename clock_type::time_point const& now) { auto& e (*pos.iterator()); @@ -2268,7 +2236,7 @@ template auto aged_unordered_container :: -emplace_hint (const_iterator const& /*hint*/, Args&&... args) -> +emplace_hint (const_iterator /*hint*/, Args&&... args) -> typename std::enable_if >::type { @@ -2298,13 +2266,11 @@ detail::aged_container_iterator aged_unordered_container :: erase (detail::aged_container_iterator < - is_const, Iterator, Base> const& pos) + is_const, Iterator, Base> pos) { - auto iter (pos.iterator()); - auto p (&*iter++); - unlink_and_delete_element (p); + unlink_and_delete_element(&*((pos++).iterator())); return detail::aged_container_iterator < - false, Iterator, Base> (iter); + false, Iterator, Base> (pos.iterator()); } template first, detail::aged_container_iterator < - is_const, Iterator, Base> const& last) + is_const, Iterator, Base> last) { - size_type n (0); - for (; first != last; ++n) - { - auto p (&*first++); - unlink_and_delete_element (p); - } + for (; first != last;) + unlink_and_delete_element(&*((first++).iterator())); + return detail::aged_container_iterator < false, Iterator, Base> (first.iterator()); } @@ -2387,6 +2350,79 @@ touch (K const& k) -> return n; } +template +template < + bool OtherIsMap, + class OtherKey, + class OtherT, + class OtherDuration, + class OtherHash, + class OtherAllocator, + bool maybe_multi +> +typename std::enable_if ::type +aged_unordered_container < + IsMulti, IsMap, Key, T, Duration, Hash, KeyEqual, Allocator>:: +operator== ( + aged_unordered_container const& other) const +{ + if (size() != other.size()) + return false; + for (auto iter (cbegin()), last (cend()), olast (other.cend()); + iter != last; ++iter) + { + auto oiter (other.find (extract (*iter))); + if (oiter == olast) + return false; + } + return true; +} + +template +template < + bool OtherIsMap, + class OtherKey, + class OtherT, + class OtherDuration, + class OtherHash, + class OtherAllocator, + bool maybe_multi +> +typename std::enable_if ::type +aged_unordered_container < + IsMulti, IsMap, Key, T, Duration, Hash, KeyEqual, Allocator>:: +operator== ( + aged_unordered_container const& other) const +{ + if (size() != other.size()) + return false; + typedef std::pair EqRng; + for (auto iter (cbegin()), last (cend()); iter != last;) + { + auto const& k (extract (*iter)); + auto const eq (equal_range (k)); + auto const oeq (other.equal_range (k)); +#if BEAST_NO_CXX14_IS_PERMUTATION + if (std::distance (eq.first, eq.second) != + std::distance (oeq.first, oeq.second) || + ! std::is_permutation (eq.first, eq.second, oeq.first)) + return false; +#else + if (! std::is_permutation (eq.first, + eq.second, oeq.first, oeq.second)) + return false; +#endif + iter = eq.second; + } + return true; +} + //------------------------------------------------------------------------------ // map, set diff --git a/beast/container/tests/aged_associative_container.test.cpp b/beast/container/tests/aged_associative_container.test.cpp index 828baeb0cc..49b43f479a 100644 --- a/beast/container/tests/aged_associative_container.test.cpp +++ b/beast/container/tests/aged_associative_container.test.cpp @@ -147,7 +147,7 @@ public: return static_cast ( ::operator new (n * sizeof(T))); } - + void deallocate (T* p, std::size_t) { ::operator delete (p); @@ -482,6 +482,23 @@ public: //-------------------------------------------------------------------------- + template + void + testIterator (); + + // Unordered containers don't have reverse iterators + template + typename std::enable_if ::type + testReverseIterator(); + + template + typename std::enable_if ::type + testReverseIterator() + { + } + + //-------------------------------------------------------------------------- + template void checkInsertCopy (Container& c, Values const& v); @@ -524,6 +541,31 @@ public: //-------------------------------------------------------------------------- + // Helpers for erase tests + template + void reverseFillAgedContainer(Container& c, Values const& v); + + template + Iter nextToEndIter (Iter const beginIter, Iter const endItr); + + //-------------------------------------------------------------------------- + + template + bool doElementErase (Container& c, Iter const beginItr, Iter const endItr); + + template + void testElementErase(); + + //-------------------------------------------------------------------------- + + template + void doRangeErase (Container& c, BeginEndSrc const& beginEndSrc); + + template + void testRangeErase(); + + //-------------------------------------------------------------------------- + // ordered template typename std::enable_if ::type @@ -1083,6 +1125,163 @@ testCopyMove () } } +//------------------------------------------------------------------------------ +// +// Iterator construction and assignment +// +//------------------------------------------------------------------------------ + +template +void +aged_associative_container_test_base:: +testIterator() +{ + typedef TestTraits Traits; + typedef typename Traits::Value Value; + typedef typename Traits::Alloc Alloc; + typename Traits::Clock clock; + auto const v (Traits::values()); + + //testcase (Traits::name() + " iterators"); + testcase ("iterator"); + + typename Traits::template Cont <> c {clock}; + + using iterator = decltype (c.begin()); + using const_iterator = decltype (c.cbegin()); + + // Should be able to construct or assign an iterator from an iterator. + iterator nnIt_0 {c.begin()}; + iterator nnIt_1 {nnIt_0}; + expect (nnIt_0 == nnIt_1, "iterator constructor failed"); + iterator nnIt_2; + nnIt_2 = nnIt_1; + expect (nnIt_1 == nnIt_2, "iterator assignment failed"); + + // Should be able to construct or assign a const_iterator from a + // const_iterator. + const_iterator ccIt_0 {c.cbegin()}; + const_iterator ccIt_1 {ccIt_0}; + expect (ccIt_0 == ccIt_1, "const_iterator constructor failed"); + const_iterator ccIt_2; + ccIt_2 = ccIt_1; + expect (ccIt_1 == ccIt_2, "const_iterator assignment failed"); + + // Comparison between iterator and const_iterator is okay + expect (nnIt_0 == ccIt_0, + "Comparing an iterator to a const_iterator failed"); + expect (ccIt_1 == nnIt_1, + "Comparing a const_iterator to an iterator failed"); + + // Should be able to construct a const_iterator from an iterator. + const_iterator ncIt_3 {c.begin()}; + const_iterator ncIt_4 {nnIt_0}; + expect (ncIt_3 == ncIt_4, + "const_iterator construction from iterator failed"); + const_iterator ncIt_5; + ncIt_5 = nnIt_2; + expect (ncIt_5 == ncIt_4, + "const_iterator assignment from iterator failed"); + + // None of these should compile because they construct or assign to a + // non-const iterator with a const_iterator. + +// iterator cnIt_0 {c.cbegin()}; + +// iterator cnIt_1 {ccIt_0}; + +// iterator cnIt_2; +// cnIt_2 = ccIt_2; +} + +template +typename std::enable_if ::type +aged_associative_container_test_base:: +testReverseIterator() +{ + typedef TestTraits Traits; + typedef typename Traits::Value Value; + typedef typename Traits::Alloc Alloc; + typename Traits::Clock clock; + auto const v (Traits::values()); + + //testcase (Traits::name() + " reverse_iterators"); + testcase ("reverse_iterator"); + + typename Traits::template Cont <> c {clock}; + + using iterator = decltype (c.begin()); + using const_iterator = decltype (c.cbegin()); + using reverse_iterator = decltype (c.rbegin()); + using const_reverse_iterator = decltype (c.crbegin()); + + // Naming decoder ring + // constructed from ------+ +----- constructed type + // /\/\ -- character pairs + // xAyBit + // r (reverse) or f (forward)--^-^ + // ^-^------ C (const) or N (non-const) + + // Should be able to construct or assign a reverse_iterator from a + // reverse_iterator. + reverse_iterator rNrNit_0 {c.rbegin()}; + reverse_iterator rNrNit_1 {rNrNit_0}; + expect (rNrNit_0 == rNrNit_1, "reverse_iterator constructor failed"); + reverse_iterator xXrNit_2; + xXrNit_2 = rNrNit_1; + expect (rNrNit_1 == xXrNit_2, "reverse_iterator assignment failed"); + + // Should be able to construct or assign a const_reverse_iterator from a + // const_reverse_iterator + const_reverse_iterator rCrCit_0 {c.crbegin()}; + const_reverse_iterator rCrCit_1 {rCrCit_0}; + expect (rCrCit_0 == rCrCit_1, "reverse_iterator constructor failed"); + const_reverse_iterator xXrCit_2; + xXrCit_2 = rCrCit_1; + expect (rCrCit_1 == xXrCit_2, "reverse_iterator assignment failed"); + + // Comparison between reverse_iterator and const_reverse_iterator is okay + expect (rNrNit_0 == rCrCit_0, + "Comparing an iterator to a const_iterator failed"); + expect (rCrCit_1 == rNrNit_1, + "Comparing a const_iterator to an iterator failed"); + + // Should be able to construct or assign a const_reverse_iterator from a + // reverse_iterator + const_reverse_iterator rNrCit_0 {c.rbegin()}; + const_reverse_iterator rNrCit_1 {rNrNit_0}; + expect (rNrCit_0 == rNrCit_1, + "const_reverse_iterator construction from reverse_iterator failed"); + xXrCit_2 = rNrNit_1; + expect (rNrCit_1 == xXrCit_2, + "const_reverse_iterator assignment from reverse_iterator failed"); + + // The standard allows these conversions: + // o reverse_iterator is explicitly constructible from iterator. + // o const_reverse_iterator is explicitly constructible from const_iterator. + // Should be able to construct or assign reverse_iterators from + // non-reverse iterators. + reverse_iterator fNrNit_0 {c.begin()}; + const_reverse_iterator fNrCit_0 {c.begin()}; + expect (fNrNit_0 == fNrCit_0, + "reverse_iterator construction from iterator failed"); + const_reverse_iterator fCrCit_0 {c.cbegin()}; + expect (fNrCit_0 == fCrCit_0, + "const_reverse_iterator construction from const_iterator failed"); + + // None of these should compile because they construct a non-reverse + // iterator from a reverse_iterator. +// iterator rNfNit_0 {c.rbegin()}; +// const_iterator rNfCit_0 {c.rbegin()}; +// const_iterator rCfCit_0 {c.crbegin()}; + + // You should not be able to assign an iterator to a reverse_iterator or + // vise-versa. So the following lines should not compile. + iterator xXfNit_0; +// xXfNit_0 = xXrNit_2; +// xXrNit_2 = xXfNit_0; +} + //------------------------------------------------------------------------------ // // Modifiers @@ -1232,9 +1431,12 @@ testChronological () c.chronological.cbegin(), c.chronological.cend(), v.begin(), v.end(), equal_value ())); - for (auto iter (v.rbegin()); iter != v.rend(); ++iter) + // Test touch() with a non-const iterator. + for (auto iter (v.crbegin()); iter != v.crend(); ++iter) { - auto found (c.find (Traits::extract (*iter))); + using iterator = typename decltype (c)::iterator; + iterator found (c.find (Traits::extract (*iter))); + expect (found != c.cend()); if (found == c.cend()) return; @@ -1243,7 +1445,30 @@ testChronological () expect (std::equal ( c.chronological.cbegin(), c.chronological.cend(), - v.rbegin(), v.rend(), equal_value ())); + v.crbegin(), v.crend(), equal_value ())); + + // Test touch() with a const_iterator + for (auto iter (v.cbegin()); iter != v.cend(); ++iter) + { + using const_iterator = typename decltype (c)::const_iterator; + const_iterator found (c.find (Traits::extract (*iter))); + + expect (found != c.cend()); + if (found == c.cend()) + return; + c.touch (found); + } + + expect (std::equal ( + c.chronological.cbegin(), c.chronological.cend(), + v.cbegin(), v.cend(), equal_value ())); + + { + // Because touch (reverse_iterator pos) is not allowed, the following + // lines should not compile for any aged_container type. +// c.touch (c.rbegin()); +// c.touch (c.crbegin()); + } } //------------------------------------------------------------------------------ @@ -1282,6 +1507,270 @@ testArrayCreate() } } +//------------------------------------------------------------------------------ +// +// Helpers for erase tests +// +//------------------------------------------------------------------------------ + +template +void +aged_associative_container_test_base:: +reverseFillAgedContainer (Container& c, Values const& values) +{ + // Just in case the passed in container was not empty. + c.clear(); + + // c.clock() returns an abstract_clock, so dynamic_cast to manual_clock. + typedef TestTraitsBase::Clock Clock; + Clock& clk (dynamic_cast (c.clock ())); + clk.set (0); + + Values rev (values); + std::sort (rev.begin (), rev.end ()); + std::reverse (rev.begin (), rev.end ()); + for (auto& v : rev) + { + // Add values in reverse order so they are reversed chronologically. + ++clk; + c.insert (v); + } +} + +// Get one iterator before endIter. We have to use operator++ because you +// cannot use operator-- with unordered container iterators. +template +Iter +aged_associative_container_test_base:: +nextToEndIter (Iter beginIter, Iter const endIter) +{ + if (beginIter == endIter) + { + fail ("Internal test failure. Cannot advance beginIter"); + return beginIter; + } + + // + Iter nextToEnd = beginIter; + do + { + nextToEnd = beginIter++; + } while (beginIter != endIter); + return nextToEnd; +} + +// Implementation for the element erase tests +// +// This test accepts: +// o the container from which we will erase elements +// o iterators into that container defining the range of the erase +// +// This implementation does not declare a pass, since it wants to allow +// the caller to examine the size of the container and the returned iterator +// +// Note that this test works on the aged_associative containers because an +// erase only invalidates references and iterators to the erased element +// (see 23.2.4/13). Therefore the passed-in end iterator stays valid through +// the whole test. +template +bool aged_associative_container_test_base:: +doElementErase (Container& c, Iter const beginItr, Iter const endItr) +{ + auto it (beginItr); + size_t count = c.size(); + while (it != endItr) + { + auto expectIt = it; + ++expectIt; + it = c.erase (it); + + if (it != expectIt) + { + fail ("Unexpected returned iterator from element erase"); + return false; + } + + --count; + if (count != c.size()) + { + fail ("Failed to erase element"); + return false; + } + + if (c.empty ()) + { + if (it != endItr) + { + fail ("Erase of last element didn't produce end"); + return false; + } + } + } + return true; +} + +//------------------------------------------------------------------------------ +// +// Erase of individual elements +// +//------------------------------------------------------------------------------ + +template +void +aged_associative_container_test_base:: +testElementErase () +{ + typedef TestTraits Traits; + + //testcase (Traits::name() + " element erase" + testcase ("element erase"); + + // Make and fill the container + typename Traits::Clock ck; + typename Traits::template Cont <> c {ck}; + reverseFillAgedContainer (c, Traits::values()); + + { + // Test standard iterators + auto tempContainer (c); + if (! doElementErase (tempContainer, + tempContainer.cbegin(), tempContainer.cend())) + return; // Test failed + + expect (tempContainer.empty(), "Failed to erase all elements"); + pass(); + } + { + // Test chronological iterators + auto tempContainer (c); + auto& chron (tempContainer.chronological); + if (! doElementErase (tempContainer, chron.begin(), chron.end())) + return; // Test failed + + expect (tempContainer.empty(), + "Failed to chronologically erase all elements"); + pass(); + } + { + // Test standard iterator partial erase + auto tempContainer (c); + expect (tempContainer.size() > 2, + "Internal failure. Container too small."); + if (! doElementErase (tempContainer, ++tempContainer.begin(), + nextToEndIter (tempContainer.begin(), tempContainer.end()))) + return; // Test failed + + expect (tempContainer.size() == 2, + "Failed to erase expected number of elements"); + pass(); + } + { + // Test chronological iterator partial erase + auto tempContainer (c); + expect (tempContainer.size() > 2, + "Internal failure. Container too small."); + auto& chron (tempContainer.chronological); + if (! doElementErase (tempContainer, ++chron.begin(), + nextToEndIter (chron.begin(), chron.end()))) + return; // Test failed + + expect (tempContainer.size() == 2, + "Failed to chronologically erase expected number of elements"); + pass(); + } + { + auto tempContainer (c); + expect (tempContainer.size() > 4, + "Internal failure. Container too small."); + // erase(reverse_iterator) is not allowed. None of the following + // should compile for any aged_container type. +// c.erase (c.rbegin()); +// c.erase (c.crbegin()); +// c.erase(c.rbegin(), ++c.rbegin()); +// c.erase(c.crbegin(), ++c.crbegin()); + } +} + +// Implementation for the range erase tests +// +// This test accepts: +// +// o A container with more than 2 elements and +// o An object to ask for begin() and end() iterators in the passed container +// +// This peculiar interface allows either the container itself to be passed as +// the second argument or the container's "chronological" element. Both +// sources of iterators need to be tested on the container. +// +// The test locates iterators such that a range-based delete leaves the first +// and last elements in the container. It then validates that the container +// ended up with the expected contents. +// +template +void +aged_associative_container_test_base:: +doRangeErase (Container& c, BeginEndSrc const& beginEndSrc) +{ + expect (c.size () > 2, + "Internal test failure. Container must have more than 2 elements"); + auto itBeginPlusOne (beginEndSrc.begin ()); + auto const valueFront = *itBeginPlusOne; + ++itBeginPlusOne; + + // Get one iterator before end() + auto itBack (nextToEndIter (itBeginPlusOne, beginEndSrc.end ())); + auto const valueBack = *itBack; + + // Erase all elements but first and last + auto const retIter = c.erase (itBeginPlusOne, itBack); + + expect (c.size() == 2, + "Unexpected size for range-erased container"); + + expect (valueFront == *(beginEndSrc.begin()), + "Unexpected first element in range-erased container"); + + expect (valueBack == *(++beginEndSrc.begin()), + "Unexpected last element in range-erased container"); + + expect (retIter == (++beginEndSrc.begin()), + "Unexpected return iterator from erase"); + + pass (); +} + +//------------------------------------------------------------------------------ +// +// Erase range of elements +// +//------------------------------------------------------------------------------ + +template +void +aged_associative_container_test_base:: +testRangeErase () +{ + typedef TestTraits Traits; + + //testcase (Traits::name() + " element erase" + testcase ("range erase"); + + // Make and fill the container + typename Traits::Clock ck; + typename Traits::template Cont <> c {ck}; + reverseFillAgedContainer (c, Traits::values()); + + // Not bothering to test range erase with reverse iterators. + { + auto tempContainer (c); + doRangeErase (tempContainer, tempContainer); + } + { + auto tempContainer (c); + doRangeErase (tempContainer, tempContainer.chronological); + } +} + //------------------------------------------------------------------------------ // // Container-wide comparison @@ -1378,9 +1867,13 @@ testMaybeUnorderedMultiMap () testConstructRange (); testConstructInitList (); testCopyMove (); + testIterator (); + testReverseIterator (); testModifiers (); testChronological (); testArrayCreate (); + testElementErase (); + testRangeErase (); testCompare (); testObservers (); } @@ -1514,4 +2007,4 @@ BEAST_DEFINE_TESTSUITE(aged_unordered_map,container,beast); BEAST_DEFINE_TESTSUITE(aged_unordered_multiset,container,beast); BEAST_DEFINE_TESTSUITE(aged_unordered_multimap,container,beast); -} +} // namespace beast