Improvements to aged_containers (RIPD-363)

- Added unit tests for element erase
- Added unit tests for range erase
- Added unit tests for touch
- Added unit tests for iterators and reverse_iterators
- Un-inlined operator== for unordered containers
- Fixed minor problems with ordered_container erase()
- Made ordered_container...
  - erase (reverse_iterator pos) not compile
  - erase (reverse_iterator first, reverse_iterator last) not compile
  - touch (reverse iterator pos) not compile
- Verified that ordered container...
  - insert() already rejects reverse_iterator
  - emplace_hint() already rejects reverse_iterator
- Made set/multiset iterators const

Regarding the set/multiset iterators, see section 1.5 of
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2913.pdf
as pointed out by Vinnie.
This commit is contained in:
Scott Schurr
2014-07-23 15:55:13 -07:00
committed by Vinnie Falco
parent ee570a49d0
commit df32f27762
4 changed files with 706 additions and 138 deletions

View File

@@ -51,20 +51,46 @@ class aged_container_iterator
public:
typedef typename Iterator::value_type::stashed::time_point time_point;
// Could be '= default', but Visual Studio 2013 chokes on it [Aug 2014]
aged_container_iterator ()
{
}
template <class OtherIterator, class OtherBase>
aged_container_iterator (aged_container_iterator <
false, OtherIterator, OtherBase> const& other)
// copy constructor
aged_container_iterator (
aged_container_iterator<is_const, Iterator, Base>
const& other) = default;
// Disable constructing a const_iterator from a non-const_iterator.
// Converting between reverse and non-reverse iterators should be explicit.
template <bool other_is_const, class OtherIterator, class OtherBase,
class = typename std::enable_if <
(other_is_const == false || is_const == true) &&
std::is_same<Iterator, OtherIterator>::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 <bool other_is_const, class OtherBase,
class = typename std::enable_if <
other_is_const == false || is_const == true>::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 <bool other_is_const, class OtherIterator, class OtherBase>
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;

View File

@@ -42,6 +42,18 @@
namespace beast {
namespace detail {
// Traits templates used to discern reverse_iterators, which are disallowed
// for mutating operations.
template <class It>
struct is_boost_reverse_iterator
: std::false_type
{};
template <class It>
struct is_boost_reverse_iterator<boost::intrusive::detail::reverse_iterator<It>>
: std::true_type
{};
/** Associative container where each element is also indexed by time.
This container mirrors the interface of the standard library ordered
@@ -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<element*>(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 <false,
// A set (that is, !IsMap) iterator is aways const because the elements
// of a set are immutable.
typedef detail::aged_container_iterator <!IsMap,
typename cont_type::iterator> iterator;
typedef detail::aged_container_iterator <true,
typename cont_type::iterator> const_iterator;
typedef detail::aged_container_iterator <false,
typedef detail::aged_container_iterator <!IsMap,
typename cont_type::reverse_iterator> reverse_iterator;
typedef detail::aged_container_iterator <true,
typename cont_type::reverse_iterator> const_reverse_iterator;
@@ -433,11 +448,13 @@ public:
class chronological_t
{
public:
typedef detail::aged_container_iterator <false,
// A set (that is, !IsMap) iterator is aways const because the elements
// of a set are immutable.
typedef detail::aged_container_iterator <!IsMap,
typename list_type::iterator> iterator;
typedef detail::aged_container_iterator <true,
typename list_type::iterator> const_iterator;
typedef detail::aged_container_iterator <false,
typedef detail::aged_container_iterator <!IsMap,
typename list_type::reverse_iterator> reverse_iterator;
typedef detail::aged_container_iterator <true,
typename list_type::reverse_iterator> const_reverse_iterator;
@@ -823,7 +840,7 @@ public:
template <bool maybe_multi = IsMulti>
typename std::enable_if <maybe_multi,
iterator>::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 <bool maybe_multi = IsMulti>
typename std::enable_if <maybe_multi,
iterator>::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 <class InputIt>
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 <bool maybe_multi = IsMulti, class... Args>
auto
emplace_hint (const_iterator const& hint, Args&&... args) ->
emplace_hint (const_iterator hint, Args&&... args) ->
typename std::enable_if <! maybe_multi,
std::pair <iterator, bool>>::type;
@@ -919,24 +936,26 @@ public:
template <bool maybe_multi = IsMulti, class... Args>
typename std::enable_if <maybe_multi,
iterator>::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 <maybe_multi> (
std::forward <Args> (args)...);
}
template <bool is_const, class Iterator, class Base>
// enable_if prevents erase (reverse_iterator pos) from compiling
template <bool is_const, class Iterator, class Base,
class = std::enable_if_t<!is_boost_reverse_iterator<Iterator>::value>>
detail::aged_container_iterator <false, Iterator, Base>
erase (detail::aged_container_iterator <
is_const, Iterator, Base> const& pos);
erase (detail::aged_container_iterator <is_const, Iterator, Base> pos);
template <bool is_const, class Iterator, class Base>
// enable_if prevents erase (reverse_iterator first, reverse_iterator last)
// from compiling
template <bool is_const, class Iterator, class Base,
class = std::enable_if_t<!is_boost_reverse_iterator<Iterator>::value>>
detail::aged_container_iterator <false, Iterator, Base>
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 <is_const, Iterator, Base> first,
detail::aged_container_iterator <is_const, Iterator, Base> last);
template <class K>
auto
@@ -948,10 +967,11 @@ public:
//--------------------------------------------------------------------------
template <bool is_const, class Iterator, class Base>
// enable_if prevents touch (reverse_iterator pos) from compiling
template <bool is_const, class Iterator, class Base,
class = std::enable_if_t<!is_boost_reverse_iterator<Iterator>::value>>
void
touch (detail::aged_container_iterator <
is_const, Iterator, Base> const& pos)
touch (detail::aged_container_iterator <is_const, Iterator, Base> pos)
{
touch (pos, clock().now());
}
@@ -1176,10 +1196,12 @@ public:
}
private:
template <bool is_const, class Iterator, class Base>
// enable_if prevents erase (reverse_iterator pos, now) from compiling
template <bool is_const, class Iterator, class Base,
class = std::enable_if_t<!is_boost_reverse_iterator<Iterator>::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 <bool maybe_propagate = std::allocator_traits <
@@ -1693,7 +1715,7 @@ template <bool IsMulti, bool IsMap, class Key, class T,
template <bool maybe_multi, class... Args>
auto
aged_ordered_container <IsMulti, IsMap, Key, T, Duration, Compare, Allocator>::
emplace_hint (const_iterator const& hint, Args&&... args) ->
emplace_hint (const_iterator hint, Args&&... args) ->
typename std::enable_if <! maybe_multi,
std::pair <iterator, bool>>::type
{
@@ -1716,36 +1738,27 @@ emplace_hint (const_iterator const& hint, Args&&... args) ->
template <bool IsMulti, bool IsMap, class Key, class T,
class Duration, class Compare, class Allocator>
template <bool is_const, class Iterator, class Base>
auto
template <bool is_const, class Iterator, class Base, class>
detail::aged_container_iterator <false, Iterator, Base>
aged_ordered_container <IsMulti, IsMap, Key, T, Duration, Compare, Allocator>::
erase (detail::aged_container_iterator <
is_const, Iterator, Base> const& pos) ->
detail::aged_container_iterator <false, Iterator, Base>
erase (detail::aged_container_iterator <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 <bool IsMulti, bool IsMap, class Key, class T,
class Duration, class Compare, class Allocator>
template <bool is_const, class Iterator, class Base>
auto
template <bool is_const, class Iterator, class Base, class>
detail::aged_container_iterator <false, Iterator, Base>
aged_ordered_container <IsMulti, IsMap, Key, T, Duration, Compare, Allocator>::
erase (detail::aged_container_iterator <
is_const, Iterator, Base> first,
detail::aged_container_iterator <
is_const, Iterator, Base> const& last) ->
detail::aged_container_iterator <false, Iterator, Base>
erase (detail::aged_container_iterator <is_const, Iterator, Base> first,
detail::aged_container_iterator <is_const, Iterator, Base> 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 <bool IsMulti, bool IsMap, class Key, class T,
class Duration, class Compare, class Allocator>
template <bool is_const, class Iterator, class Base>
template <bool is_const, class Iterator, class Base, class>
void
aged_ordered_container <IsMulti, IsMap, Key, T, Duration, Compare, Allocator>::
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());

View File

@@ -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<element*>(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 <false,
// A set (that is, !IsMap) iterator is aways const because the elements
// of a set are immutable.
typedef detail::aged_container_iterator <!IsMap,
typename cont_type::iterator> iterator;
typedef detail::aged_container_iterator <true,
typename cont_type::iterator> const_iterator;
typedef detail::aged_container_iterator <false,
typedef detail::aged_container_iterator <!IsMap,
typename cont_type::local_iterator> local_iterator;
typedef detail::aged_container_iterator <true,
typename cont_type::local_iterator> const_local_iterator;
@@ -631,11 +634,13 @@ public:
class chronological_t
{
public:
typedef detail::aged_container_iterator <false,
// A set (that is, !IsMap) iterator is aways const because the elements
// of a set are immutable.
typedef detail::aged_container_iterator <!IsMap,
typename list_type::iterator> iterator;
typedef detail::aged_container_iterator <true,
typename list_type::iterator> const_iterator;
typedef detail::aged_container_iterator <false,
typedef detail::aged_container_iterator <!IsMap,
typename list_type::reverse_iterator> reverse_iterator;
typedef detail::aged_container_iterator <true,
typename list_type::reverse_iterator> const_reverse_iterator;
@@ -1021,7 +1026,7 @@ public:
template <bool maybe_multi = IsMulti>
typename std::enable_if <maybe_multi,
iterator>::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 <bool maybe_multi = IsMulti>
typename std::enable_if <maybe_multi,
iterator>::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 <class InputIt>
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 <bool maybe_multi = IsMulti, class... Args>
auto
emplace_hint (const_iterator const& /*hint*/, Args&&... args) ->
emplace_hint (const_iterator /*hint*/, Args&&... args) ->
typename std::enable_if <! maybe_multi,
std::pair <iterator, bool>>::type;
@@ -1121,7 +1126,7 @@ public:
template <bool maybe_multi = IsMulti, class... Args>
typename std::enable_if <maybe_multi,
iterator>::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 <bool is_const, class Iterator, class Base>
detail::aged_container_iterator <false, Iterator, Base>
erase (detail::aged_container_iterator <
is_const, Iterator, Base> const& pos);
is_const, Iterator, Base> pos);
template <bool is_const, class Iterator, class Base>
detail::aged_container_iterator <false, Iterator, Base>
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 <class K>
auto
@@ -1152,7 +1157,7 @@ public:
template <bool is_const, class Iterator, class Base>
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 <! maybe_multi,
bool>::type
typename std::enable_if <! maybe_multi, bool>::type
operator== (
aged_unordered_container <false, OtherIsMap,
OtherKey, OtherT, OtherDuration, OtherHash, KeyEqual,
OtherAllocator> 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 <maybe_multi,
bool>::type
typename std::enable_if <maybe_multi, bool>::type
operator== (
aged_unordered_container <true, OtherIsMap,
OtherKey, OtherT, OtherDuration, OtherHash, KeyEqual,
OtherAllocator> const& other) const
{
if (size() != other.size())
return false;
typedef std::pair <const_iterator, const_iterator> 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 <class InputIt>
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 <class InputIt>
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 <class InputIt>
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 <bool is_const, class Iterator, class Base>
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 <bool maybe_multi, class... Args>
auto
aged_unordered_container <IsMulti, IsMap, Key, T, Duration,
Hash, KeyEqual, Allocator>::
emplace_hint (const_iterator const& /*hint*/, Args&&... args) ->
emplace_hint (const_iterator /*hint*/, Args&&... args) ->
typename std::enable_if <! maybe_multi,
std::pair <iterator, bool>>::type
{
@@ -2298,13 +2266,11 @@ detail::aged_container_iterator <false, Iterator, Base>
aged_unordered_container <IsMulti, IsMap, Key, T, Duration,
Hash, KeyEqual, Allocator>::
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 <bool IsMulti, bool IsMap, class Key, class T,
@@ -2316,14 +2282,11 @@ aged_unordered_container <IsMulti, IsMap, Key, T, Duration,
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)
{
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 <bool IsMulti, bool IsMap, class Key, class T,
class Duration, class Hash, class KeyEqual, class Allocator>
template <
bool OtherIsMap,
class OtherKey,
class OtherT,
class OtherDuration,
class OtherHash,
class OtherAllocator,
bool maybe_multi
>
typename std::enable_if <! maybe_multi, bool>::type
aged_unordered_container <
IsMulti, IsMap, Key, T, Duration, Hash, KeyEqual, Allocator>::
operator== (
aged_unordered_container <false, OtherIsMap,
OtherKey, OtherT, OtherDuration, OtherHash, KeyEqual,
OtherAllocator> 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 <bool IsMulti, bool IsMap, class Key, class T,
class Duration, class Hash, class KeyEqual, class Allocator>
template <
bool OtherIsMap,
class OtherKey,
class OtherT,
class OtherDuration,
class OtherHash,
class OtherAllocator,
bool maybe_multi
>
typename std::enable_if <maybe_multi, bool>::type
aged_unordered_container <
IsMulti, IsMap, Key, T, Duration, Hash, KeyEqual, Allocator>::
operator== (
aged_unordered_container <true, OtherIsMap,
OtherKey, OtherT, OtherDuration, OtherHash, KeyEqual,
OtherAllocator> const& other) const
{
if (size() != other.size())
return false;
typedef std::pair <const_iterator, const_iterator> 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

View File

@@ -482,6 +482,23 @@ public:
//--------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testIterator ();
// Unordered containers don't have reverse iterators
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if <! IsUnordered>::type
testReverseIterator();
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if <IsUnordered>::type
testReverseIterator()
{
}
//--------------------------------------------------------------------------
template <class Container, class Values>
void checkInsertCopy (Container& c, Values const& v);
@@ -524,6 +541,31 @@ public:
//--------------------------------------------------------------------------
// Helpers for erase tests
template <class Container, class Values>
void reverseFillAgedContainer(Container& c, Values const& v);
template <class Iter>
Iter nextToEndIter (Iter const beginIter, Iter const endItr);
//--------------------------------------------------------------------------
template <class Container, class Iter>
bool doElementErase (Container& c, Iter const beginItr, Iter const endItr);
template <bool IsUnordered, bool IsMulti, bool IsMap>
void testElementErase();
//--------------------------------------------------------------------------
template <class Container, class BeginEndSrc>
void doRangeErase (Container& c, BeginEndSrc const& beginEndSrc);
template <bool IsUnordered, bool IsMulti, bool IsMap>
void testRangeErase();
//--------------------------------------------------------------------------
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if <! IsUnordered>::type
@@ -1083,6 +1125,163 @@ testCopyMove ()
}
}
//------------------------------------------------------------------------------
//
// Iterator construction and assignment
//
//------------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::
testIterator()
{
typedef TestTraits <IsUnordered, IsMulti, IsMap> 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 <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if <! IsUnordered>::type
aged_associative_container_test_base::
testReverseIterator()
{
typedef TestTraits <IsUnordered, IsMulti, IsMap> 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 <Traits> ()));
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 <Traits> ()));
v.crbegin(), v.crend(), equal_value <Traits> ()));
// 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 <Traits> ()));
{
// 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 <class Container, class Values>
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 <Clock&> (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 <class Iter>
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 <class Container, class Iter>
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 <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::
testElementErase ()
{
typedef TestTraits <IsUnordered, IsMulti, IsMap> 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 <class Container, class BeginEndSrc>
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 <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::
testRangeErase ()
{
typedef TestTraits <IsUnordered, IsMulti, IsMap> 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 <IsUnordered, IsMulti, IsMap> ();
testConstructInitList <IsUnordered, IsMulti, IsMap> ();
testCopyMove <IsUnordered, IsMulti, IsMap> ();
testIterator <IsUnordered, IsMulti, IsMap> ();
testReverseIterator <IsUnordered, IsMulti, IsMap> ();
testModifiers <IsUnordered, IsMulti, IsMap> ();
testChronological <IsUnordered, IsMulti, IsMap> ();
testArrayCreate <IsUnordered, IsMulti, IsMap> ();
testElementErase <IsUnordered, IsMulti, IsMap> ();
testRangeErase <IsUnordered, IsMulti, IsMap> ();
testCompare <IsUnordered, IsMulti, IsMap> ();
testObservers <IsUnordered, IsMulti, IsMap> ();
}
@@ -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