f92ab29ffa
2016-04-29 Chris Gregory <czipperz@gmail.com> * config/*: Remove trailing whitespace. * src/*: Likewise. * testsuite/tr1/*: Likewise. * testsuite/util/*: Likewise. From-SVN: r235645
1577 lines
40 KiB
C++
1577 lines
40 KiB
C++
// -*- C++ -*-
|
|
|
|
// Copyright (C) 2009-2016 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the terms
|
|
// of the GNU General Public License as published by the Free Software
|
|
// Foundation; either version 3, or (at your option) any later
|
|
// version.
|
|
|
|
// This library is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this library; see the file COPYING3. If not see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef _GLIBCXX_EXCEPTION_SAFETY_H
|
|
#define _GLIBCXX_EXCEPTION_SAFETY_H
|
|
|
|
#include <testsuite_container_traits.h>
|
|
#include <ext/throw_allocator.h>
|
|
|
|
// Container requirement testing.
|
|
namespace __gnu_test
|
|
{
|
|
// Base class for exception testing, contains utilities.
|
|
struct setup_base
|
|
{
|
|
typedef std::size_t size_type;
|
|
typedef std::uniform_int_distribution<size_type> distribution_type;
|
|
typedef std::mt19937 engine_type;
|
|
|
|
// Return randomly generated integer on range [0, __max_size].
|
|
static size_type
|
|
generate(size_type __max_size)
|
|
{
|
|
// Make the generator static...
|
|
const engine_type engine;
|
|
const distribution_type distribution;
|
|
static auto generator = std::bind(distribution, engine,
|
|
std::placeholders::_1);
|
|
|
|
// ... but set the range for this particular invocation here.
|
|
const typename distribution_type::param_type p(0, __max_size);
|
|
size_type random = generator(p);
|
|
if (random < distribution.min() || random > distribution.max())
|
|
std::__throw_out_of_range_fmt(__N("setup_base::generate\n"
|
|
"random number generated is: %zu "
|
|
"out of range [%zu, %zu]\n"),
|
|
(size_t)random,
|
|
(size_t)distribution.min(),
|
|
(size_t)distribution.max());
|
|
return random;
|
|
}
|
|
|
|
// Given an instantiating type, return a unique value.
|
|
template<typename _Tp>
|
|
struct generate_unique
|
|
{
|
|
typedef _Tp value_type;
|
|
|
|
operator value_type()
|
|
{
|
|
static value_type __ret;
|
|
++__ret;
|
|
return __ret;
|
|
}
|
|
};
|
|
|
|
// Partial specialization for pair.
|
|
template<typename _Tp1, typename _Tp2>
|
|
struct generate_unique<std::pair<const _Tp1, _Tp2>>
|
|
{
|
|
typedef _Tp1 first_type;
|
|
typedef _Tp2 second_type;
|
|
typedef std::pair<const _Tp1, _Tp2> pair_type;
|
|
|
|
operator pair_type()
|
|
{
|
|
static first_type _S_1;
|
|
static second_type _S_2;
|
|
++_S_1;
|
|
++_S_2;
|
|
return pair_type(_S_1, _S_2);
|
|
}
|
|
};
|
|
|
|
// Partial specialization for throw_value
|
|
template<typename _Cond>
|
|
struct generate_unique<__gnu_cxx::throw_value_base<_Cond>>
|
|
{
|
|
typedef __gnu_cxx::throw_value_base<_Cond> value_type;
|
|
|
|
operator value_type()
|
|
{
|
|
static size_t _S_i(0);
|
|
return value_type(_S_i++);
|
|
}
|
|
};
|
|
|
|
|
|
// Construct container of size n directly. _Tp == container type.
|
|
template<typename _Tp>
|
|
struct make_container_base
|
|
{
|
|
_Tp _M_container;
|
|
|
|
make_container_base() = default;
|
|
make_container_base(const size_type n): _M_container(n) { }
|
|
|
|
operator _Tp&() { return _M_container; }
|
|
};
|
|
|
|
// Construct container of size n, via multiple insertions. For
|
|
// associated and unordered types, unique value_type elements are
|
|
// necessary.
|
|
template<typename _Tp, bool = traits<_Tp>::is_mapped::value>
|
|
struct make_insert_container_base
|
|
: public make_container_base<_Tp>
|
|
{
|
|
using make_container_base<_Tp>::_M_container;
|
|
typedef typename _Tp::value_type value_type;
|
|
|
|
make_insert_container_base(const size_type n)
|
|
{
|
|
for (size_type i = 0; i < n; ++i)
|
|
{
|
|
value_type v = generate_unique<value_type>();
|
|
_M_container.insert(v);
|
|
}
|
|
assert(_M_container.size() == n);
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
struct make_insert_container_base<_Tp, false>
|
|
: public make_container_base<_Tp>
|
|
{
|
|
using make_container_base<_Tp>::_M_container;
|
|
typedef typename _Tp::value_type value_type;
|
|
|
|
make_insert_container_base(const size_type n)
|
|
{
|
|
for (size_type i = 0; i < n; ++i)
|
|
{
|
|
value_type v = generate_unique<value_type>();
|
|
_M_container.insert(_M_container.end(), v);
|
|
}
|
|
assert(_M_container.size() == n);
|
|
}
|
|
};
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_size_type_constructor::value>
|
|
struct make_container_n;
|
|
|
|
// Specialization for non-associative types that have a constructor with
|
|
// a size argument.
|
|
template<typename _Tp>
|
|
struct make_container_n<_Tp, true>
|
|
: public make_container_base<_Tp>
|
|
{
|
|
make_container_n(const size_type n) : make_container_base<_Tp>(n) { }
|
|
};
|
|
|
|
template<typename _Tp>
|
|
struct make_container_n<_Tp, false>
|
|
: public make_insert_container_base<_Tp>
|
|
{
|
|
make_container_n(const size_type n)
|
|
: make_insert_container_base<_Tp>(n) { }
|
|
};
|
|
|
|
|
|
// Randomly size and populate a given container reference.
|
|
// NB: Responsibility for turning off exceptions lies with caller.
|
|
template<typename _Tp, bool = traits<_Tp>::is_allocator_aware::value>
|
|
struct populate
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::allocator_type allocator_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
populate(_Tp& __container)
|
|
{
|
|
const allocator_type a = __container.get_allocator();
|
|
|
|
// Size test container.
|
|
const size_type max_elements = 100;
|
|
size_type n = generate(max_elements);
|
|
|
|
// Construct new container.
|
|
make_container_n<container_type> made(n);
|
|
container_type& tmp = made;
|
|
std::swap(tmp, __container);
|
|
}
|
|
};
|
|
|
|
// Partial specialization, empty.
|
|
template<typename _Tp>
|
|
struct populate<_Tp, false>
|
|
{
|
|
populate(_Tp&) { }
|
|
};
|
|
|
|
// Compare two containers for equivalence.
|
|
// Right now, that means size.
|
|
// Returns true if equal, throws if not.
|
|
template<typename _Tp>
|
|
static bool
|
|
compare(const _Tp& __control, const _Tp& __test)
|
|
{
|
|
// Make sure test container is in a consistent state, as
|
|
// compared to the control container.
|
|
// NB: Should be equivalent to __test != __control, but
|
|
// computed without equivalence operators
|
|
const size_type szt
|
|
= std::distance(__test.begin(), __test.end());
|
|
const size_type szc
|
|
= std::distance(__control.begin(), __control.end());
|
|
|
|
if (szt != szc)
|
|
throw std::logic_error(
|
|
"setup_base::compare containers size not equal");
|
|
|
|
// Should test iterator validity before and after exception.
|
|
bool __equal_it = std::equal(__test.begin(), __test.end(),
|
|
__control.begin());
|
|
|
|
if (!__equal_it)
|
|
throw std::logic_error(
|
|
"setup_base::compare containers iterators not equal");
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
// Containing structure holding functors.
|
|
struct functor_base : public setup_base
|
|
{
|
|
// Abstract the erase function.
|
|
template<typename _Tp>
|
|
struct erase_base
|
|
{
|
|
typedef typename _Tp::iterator iterator;
|
|
typedef typename _Tp::const_iterator const_iterator;
|
|
|
|
iterator (_Tp::* _F_erase_point)(const_iterator);
|
|
iterator (_Tp::* _F_erase_range)(const_iterator, const_iterator);
|
|
|
|
erase_base()
|
|
: _F_erase_point(&_Tp::erase), _F_erase_range(&_Tp::erase) { }
|
|
};
|
|
|
|
#if _GLIBCXX_USE_CXX11_ABI == 0 || __cplusplus < 201103L
|
|
// Specialization, old C++03 signature.
|
|
template<typename _Tp1, typename _Tp2, typename _Tp3>
|
|
struct erase_base<std::basic_string<_Tp1, _Tp2, _Tp3>>
|
|
{
|
|
typedef std::basic_string<_Tp1, _Tp2, _Tp3> container_type;
|
|
typedef typename container_type::iterator iterator;
|
|
|
|
iterator (container_type::* _F_erase_point)(iterator);
|
|
iterator (container_type::* _F_erase_range)(iterator, iterator);
|
|
|
|
erase_base()
|
|
: _F_erase_point(&container_type::erase),
|
|
_F_erase_range(&container_type::erase) { }
|
|
};
|
|
#endif
|
|
|
|
// Specialization, as forward_list has erase_after.
|
|
template<typename _Tp1, typename _Tp2>
|
|
struct erase_base<std::forward_list<_Tp1, _Tp2>>
|
|
{
|
|
typedef std::forward_list<_Tp1, _Tp2> container_type;
|
|
typedef typename container_type::iterator iterator;
|
|
typedef typename container_type::const_iterator const_iterator;
|
|
|
|
iterator (container_type::* _F_erase_point)(const_iterator);
|
|
iterator (container_type::* _F_erase_range)(const_iterator,
|
|
const_iterator);
|
|
|
|
erase_base()
|
|
: _F_erase_point(&container_type::erase_after),
|
|
_F_erase_range(&container_type::erase_after) { }
|
|
};
|
|
|
|
template<typename _Tp,
|
|
bool = traits<_Tp>::has_erase::value,
|
|
bool = traits<_Tp>::has_erase_after::value>
|
|
struct erase_point;
|
|
|
|
// Specialization for most containers.
|
|
template<typename _Tp>
|
|
struct erase_point<_Tp, true, false> : public erase_base<_Tp>
|
|
{
|
|
using erase_base<_Tp>::_F_erase_point;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
// NB: Should be equivalent to size() member function, but
|
|
// computed with begin() and end().
|
|
const size_type sz = std::distance(__container.begin(),
|
|
__container.end());
|
|
|
|
// NB: Lowest common denominator: use forward iterator operations.
|
|
auto i = __container.begin();
|
|
std::advance(i, generate(sz));
|
|
|
|
// Makes it easier to think of this as __container.erase(i)
|
|
(__container.*_F_erase_point)(i);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization for forward_list.
|
|
template<typename _Tp>
|
|
struct erase_point<_Tp, false, true> : public erase_base<_Tp>
|
|
{
|
|
using erase_base<_Tp>::_F_erase_point;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
// NB: Should be equivalent to size() member function, but
|
|
// computed with begin() and end().
|
|
const size_type sz = std::distance(__container.begin(),
|
|
__container.end());
|
|
|
|
// NB: Lowest common denominator: use forward iterator operations.
|
|
auto i = __container.before_begin();
|
|
std::advance(i, generate(sz));
|
|
|
|
// Makes it easier to think of this as __container.erase(i)
|
|
(__container.*_F_erase_point)(i);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct erase_point<_Tp, false, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp,
|
|
bool = traits<_Tp>::has_erase::value,
|
|
bool = traits<_Tp>::has_erase_after::value>
|
|
struct erase_range;
|
|
|
|
// Specialization for most containers.
|
|
template<typename _Tp>
|
|
struct erase_range<_Tp, true, false> : public erase_base<_Tp>
|
|
{
|
|
using erase_base<_Tp>::_F_erase_range;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
const size_type sz = std::distance(__container.begin(),
|
|
__container.end());
|
|
size_type s1 = generate(sz);
|
|
size_type s2 = generate(sz);
|
|
auto i1 = __container.begin();
|
|
auto i2 = __container.begin();
|
|
std::advance(i1, std::min(s1, s2));
|
|
std::advance(i2, std::max(s1, s2));
|
|
|
|
// Makes it easier to think of this as __container.erase(i1, i2).
|
|
(__container.*_F_erase_range)(i1, i2);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization for forward_list.
|
|
template<typename _Tp>
|
|
struct erase_range<_Tp, false, true> : public erase_base<_Tp>
|
|
{
|
|
using erase_base<_Tp>::_F_erase_range;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
const size_type sz = std::distance(__container.begin(),
|
|
__container.end());
|
|
size_type s1 = generate(sz);
|
|
size_type s2 = generate(sz);
|
|
auto i1 = __container.before_begin();
|
|
auto i2 = __container.before_begin();
|
|
std::advance(i1, std::min(s1, s2));
|
|
std::advance(i2, std::max(s1, s2));
|
|
|
|
// Makes it easier to think of this as __container.erase(i1, i2).
|
|
(__container.*_F_erase_range)(i1, i2);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct erase_range<_Tp, false, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_push_pop::value>
|
|
struct pop_front
|
|
{
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
__container.pop_front();
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct pop_front<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_push_pop::value
|
|
&& traits<_Tp>::is_reversible::value>
|
|
struct pop_back
|
|
{
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
__container.pop_back();
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct pop_back<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_push_pop::value>
|
|
struct push_front
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.push_front(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.push_front(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct push_front<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_push_pop::value
|
|
&& traits<_Tp>::is_reversible::value>
|
|
struct push_back
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.push_back(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.push_back(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct push_back<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_push_pop::value
|
|
&& traits<_Tp>::has_emplace::value>
|
|
struct emplace_front
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.emplace_front(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.emplace_front(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct emplace_front<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_push_pop::value
|
|
&& traits<_Tp>::has_emplace::value
|
|
&& traits<_Tp>::is_reversible::value>
|
|
struct emplace_back
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.emplace_back(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.push_back(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct emplace_back<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
|
|
// Abstract the insert function into two parts:
|
|
// 1, insert_base_functions == holds function pointer
|
|
// 2, insert_base == links function pointer to class insert method
|
|
template<typename _Tp>
|
|
struct insert_base
|
|
{
|
|
typedef typename _Tp::iterator iterator;
|
|
typedef typename _Tp::const_iterator const_iterator;
|
|
typedef typename _Tp::value_type value_type;
|
|
|
|
iterator (_Tp::* _F_insert_point)(const_iterator, const value_type&);
|
|
|
|
insert_base() : _F_insert_point(&_Tp::insert) { }
|
|
};
|
|
|
|
// Specialization, old C++03 signature.
|
|
template<typename _Tp1, typename _Tp2, typename _Tp3>
|
|
struct insert_base<std::basic_string<_Tp1, _Tp2, _Tp3>>
|
|
{
|
|
typedef std::basic_string<_Tp1, _Tp2, _Tp3> container_type;
|
|
typedef typename container_type::iterator iterator;
|
|
typedef typename container_type::const_iterator const_iterator;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
#if _GLIBCXX_USE_CXX11_ABI == 0 || __cplusplus < 201103L
|
|
iterator (container_type::* _F_insert_point)(iterator, value_type);
|
|
#else
|
|
iterator (container_type::* _F_insert_point)(const_iterator,
|
|
value_type);
|
|
#endif
|
|
|
|
insert_base() : _F_insert_point(&container_type::insert) { }
|
|
};
|
|
|
|
// Specialization, by value.
|
|
template<typename _Tp1, typename _Tp2, typename _Tp3,
|
|
template <typename, typename, typename> class _Tp4>
|
|
struct insert_base<__gnu_cxx::__versa_string<_Tp1, _Tp2, _Tp3, _Tp4>>
|
|
{
|
|
typedef __gnu_cxx::__versa_string<_Tp1, _Tp2, _Tp3, _Tp4>
|
|
container_type;
|
|
typedef typename container_type::iterator iterator;
|
|
typedef typename container_type::const_iterator const_iterator;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
iterator (container_type::* _F_insert_point)(const_iterator,
|
|
value_type);
|
|
|
|
insert_base() : _F_insert_point(&container_type::insert) { }
|
|
};
|
|
|
|
// Specialization, as forward_list has insert_after.
|
|
template<typename _Tp1, typename _Tp2>
|
|
struct insert_base<std::forward_list<_Tp1, _Tp2>>
|
|
{
|
|
typedef std::forward_list<_Tp1, _Tp2> container_type;
|
|
typedef typename container_type::iterator iterator;
|
|
typedef typename container_type::const_iterator const_iterator;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
iterator (container_type::* _F_insert_point)(const_iterator,
|
|
const value_type&);
|
|
|
|
insert_base() : _F_insert_point(&container_type::insert_after) { }
|
|
};
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_insert::value,
|
|
bool = traits<_Tp>::has_insert_after::value>
|
|
struct insert_point;
|
|
|
|
// Specialization for most containers.
|
|
template<typename _Tp>
|
|
struct insert_point<_Tp, true, false> : public insert_base<_Tp>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
using insert_base<_Tp>::_F_insert_point;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.begin();
|
|
std::advance(i, s);
|
|
(__test.*_F_insert_point)(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.begin();
|
|
std::advance(i, s);
|
|
(__test.*_F_insert_point)(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization for forward_list.
|
|
template<typename _Tp>
|
|
struct insert_point<_Tp, false, true> : public insert_base<_Tp>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
using insert_base<_Tp>::_F_insert_point;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.before_begin();
|
|
std::advance(i, s);
|
|
(__test.*_F_insert_point)(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.before_begin();
|
|
std::advance(i, s);
|
|
(__test.*_F_insert_point)(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct insert_point<_Tp, false, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_emplace::value
|
|
&& (traits<_Tp>::is_associative::value
|
|
|| traits<_Tp>::is_unordered::value)>
|
|
struct emplace;
|
|
|
|
// Specialization for associative and unordered containers.
|
|
template<typename _Tp>
|
|
struct emplace<_Tp, true>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
typedef typename container_type::size_type size_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.emplace(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
__test.emplace(cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct emplace<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::has_emplace::value,
|
|
bool = traits<_Tp>::is_associative::value
|
|
|| traits<_Tp>::is_unordered::value,
|
|
bool = traits<_Tp>::has_insert_after::value>
|
|
struct emplace_point;
|
|
|
|
// Specialization for most containers.
|
|
template<typename _Tp>
|
|
struct emplace_point<_Tp, true, false, false>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.begin();
|
|
std::advance(i, s);
|
|
__test.emplace(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.begin();
|
|
std::advance(i, s);
|
|
__test.emplace(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization for associative and unordered containers.
|
|
template<typename _Tp>
|
|
struct emplace_point<_Tp, true, true, false>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.begin();
|
|
std::advance(i, s);
|
|
__test.emplace_hint(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.begin();
|
|
std::advance(i, s);
|
|
__test.emplace_hint(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization for forward_list.
|
|
template<typename _Tp>
|
|
struct emplace_point<_Tp, true, false, true>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::value_type value_type;
|
|
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.before_begin();
|
|
std::advance(i, s);
|
|
__test.emplace_after(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
// Assumes containers start out equivalent.
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
const value_type cv = generate_unique<value_type>();
|
|
const size_type sz = std::distance(__test.begin(), __test.end());
|
|
size_type s = generate(sz);
|
|
auto i = __test.before_begin();
|
|
std::advance(i, s);
|
|
__test.emplace_after(i, cv);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp, bool is_associative_or_unordered,
|
|
bool has_insert_after>
|
|
struct emplace_point<_Tp, false, is_associative_or_unordered,
|
|
has_insert_after>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::is_associative::value
|
|
|| traits<_Tp>::is_unordered::value>
|
|
struct clear
|
|
{
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
__container.clear();
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct clear<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp, bool = traits<_Tp>::is_unordered::value>
|
|
struct rehash
|
|
{
|
|
void
|
|
operator()(_Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
size_type s = generate(__test.bucket_count());
|
|
__test.rehash(s);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
|
|
void
|
|
operator()(_Tp& __control, _Tp& __test)
|
|
{
|
|
try
|
|
{
|
|
size_type s = generate(__test.bucket_count());
|
|
__test.rehash(s);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{
|
|
// Also check hash status.
|
|
bool fail(false);
|
|
if (__control.load_factor() != __test.load_factor())
|
|
fail = true;
|
|
if (__control.max_load_factor() != __test.max_load_factor())
|
|
fail = true;
|
|
if (__control.bucket_count() != __test.bucket_count())
|
|
fail = true;
|
|
if (__control.max_bucket_count() != __test.max_bucket_count())
|
|
fail = true;
|
|
|
|
if (fail)
|
|
{
|
|
char buf[40];
|
|
std::string __s("setup_base::rehash "
|
|
"containers not equal");
|
|
__s += "\n";
|
|
__s += "\n";
|
|
__s += "\t\t\tcontrol : test";
|
|
__s += "\n";
|
|
__s += "load_factor\t\t";
|
|
__builtin_sprintf(buf, "%lu", __control.load_factor());
|
|
__s += buf;
|
|
__s += " : ";
|
|
__builtin_sprintf(buf, "%lu", __test.load_factor());
|
|
__s += buf;
|
|
__s += "\n";
|
|
|
|
__s += "max_load_factor\t\t";
|
|
__builtin_sprintf(buf, "%lu", __control.max_load_factor());
|
|
__s += buf;
|
|
__s += " : ";
|
|
__builtin_sprintf(buf, "%lu", __test.max_load_factor());
|
|
__s += buf;
|
|
__s += "\n";
|
|
|
|
__s += "bucket_count\t\t";
|
|
__builtin_sprintf(buf, "%lu", __control.bucket_count());
|
|
__s += buf;
|
|
__s += " : ";
|
|
__builtin_sprintf(buf, "%lu", __test.bucket_count());
|
|
__s += buf;
|
|
__s += "\n";
|
|
|
|
__s += "max_bucket_count\t";
|
|
__builtin_sprintf(buf, "%lu", __control.max_bucket_count());
|
|
__s += buf;
|
|
__s += " : ";
|
|
__builtin_sprintf(buf, "%lu", __test.max_bucket_count());
|
|
__s += buf;
|
|
__s += "\n";
|
|
|
|
std::__throw_logic_error(__s.c_str());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Specialization, empty.
|
|
template<typename _Tp>
|
|
struct rehash<_Tp, false>
|
|
{
|
|
void
|
|
operator()(_Tp&) { }
|
|
|
|
void
|
|
operator()(_Tp&, _Tp&) { }
|
|
};
|
|
|
|
|
|
template<typename _Tp>
|
|
struct swap
|
|
{
|
|
_Tp _M_other;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
__container.swap(_M_other);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
|
|
template<typename _Tp>
|
|
struct iterator_operations
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::iterator iterator;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
// Any will do.
|
|
iterator i = __container.begin();
|
|
iterator __attribute__((unused)) icopy(i);
|
|
iterator __attribute__((unused)) iassign = i;
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
|
|
template<typename _Tp>
|
|
struct const_iterator_operations
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef typename container_type::const_iterator const_iterator;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
// Any will do.
|
|
const_iterator i = __container.begin();
|
|
const_iterator __attribute__((unused)) icopy(i);
|
|
const_iterator __attribute__((unused)) iassign = i;
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
struct assign_operator
|
|
{
|
|
_Tp _M_other;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
// An exception while assigning might leave the container empty
|
|
// making future attempts less relevant. So we copy it before to
|
|
// always assign to a non empty container. It also check for copy
|
|
// constructor exception safety at the same time.
|
|
_Tp __clone(__container);
|
|
__clone = _M_other;
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
|
|
|
|
#if __cplusplus >= 201103L
|
|
template<typename _Tp>
|
|
struct move_assign_operator
|
|
{
|
|
_Tp _M_other;
|
|
|
|
void
|
|
operator()(_Tp& __container)
|
|
{
|
|
try
|
|
{
|
|
__container = std::move(_M_other);
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{ throw; }
|
|
}
|
|
};
|
|
#endif
|
|
};
|
|
|
|
// Base class for exception tests.
|
|
template<typename _Tp>
|
|
struct test_base: public functor_base
|
|
{
|
|
typedef _Tp container_type;
|
|
|
|
typedef functor_base base_type;
|
|
typedef populate<container_type> populate;
|
|
typedef make_container_n<container_type> make_container_n;
|
|
|
|
typedef clear<container_type> clear;
|
|
typedef erase_point<container_type> erase_point;
|
|
typedef erase_range<container_type> erase_range;
|
|
typedef insert_point<container_type> insert_point;
|
|
typedef emplace<container_type> emplace;
|
|
typedef emplace_point<container_type> emplace_point;
|
|
typedef emplace_front<container_type> emplace_front;
|
|
typedef emplace_back<container_type> emplace_back;
|
|
typedef pop_front<container_type> pop_front;
|
|
typedef pop_back<container_type> pop_back;
|
|
typedef push_front<container_type> push_front;
|
|
typedef push_back<container_type> push_back;
|
|
typedef rehash<container_type> rehash;
|
|
typedef swap<container_type> swap;
|
|
typedef iterator_operations<container_type> iterator_ops;
|
|
typedef const_iterator_operations<container_type> const_iterator_ops;
|
|
typedef assign_operator<container_type> assign_operator;
|
|
#if __cplusplus >= 201103L
|
|
typedef move_assign_operator<container_type> move_assign_operator;
|
|
#endif
|
|
|
|
using base_type::compare;
|
|
};
|
|
|
|
|
|
// Run through all member functions for basic exception safety
|
|
// guarantee: no resource leaks when exceptions are thrown.
|
|
//
|
|
// Types of resources checked: memory.
|
|
//
|
|
// For each member function, use throw_value and throw_allocator as
|
|
// value_type and allocator_type to force potential exception safety
|
|
// errors.
|
|
//
|
|
// NB: Assumes
|
|
// _Tp::value_type is __gnu_cxx::throw_value_*
|
|
// _Tp::allocator_type is __gnu_cxx::throw_allocator_*
|
|
// And that the _Cond template parameter for them both is
|
|
// __gnu_cxx::limit_condition.
|
|
template<typename _Tp>
|
|
struct basic_safety : public test_base<_Tp>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef test_base<container_type> base_type;
|
|
typedef typename base_type::populate populate;
|
|
typedef std::function<void(container_type&)> function_type;
|
|
typedef __gnu_cxx::limit_condition condition_type;
|
|
|
|
using base_type::generate;
|
|
|
|
basic_safety() { run(); }
|
|
|
|
void
|
|
run()
|
|
{
|
|
{
|
|
// Setup.
|
|
condition_type::never_adjustor off;
|
|
|
|
// Construct containers.
|
|
container_type container;
|
|
populate p1(container);
|
|
|
|
// Construct list of member functions to exercise.
|
|
std::vector<function_type> functions;
|
|
typename base_type::iterator_ops iops;
|
|
functions.push_back(function_type(iops));
|
|
typename base_type::const_iterator_ops ciops;
|
|
functions.push_back(function_type(ciops));
|
|
|
|
typename base_type::erase_point erasep;
|
|
functions.push_back(function_type(erasep));
|
|
typename base_type::erase_range eraser;
|
|
functions.push_back(function_type(eraser));
|
|
typename base_type::insert_point insertp;
|
|
functions.push_back(function_type(insertp));
|
|
typename base_type::emplace emplace;
|
|
functions.push_back(function_type(emplace));
|
|
typename base_type::emplace_point emplacep;
|
|
functions.push_back(function_type(emplacep));
|
|
typename base_type::emplace_front emplacef;
|
|
functions.push_back(function_type(emplacef));
|
|
typename base_type::emplace_back emplaceb;
|
|
functions.push_back(function_type(emplaceb));
|
|
typename base_type::pop_front popf;
|
|
functions.push_back(function_type(popf));
|
|
typename base_type::pop_back popb;
|
|
functions.push_back(function_type(popb));
|
|
typename base_type::push_front pushf;
|
|
functions.push_back(function_type(pushf));
|
|
typename base_type::push_back pushb;
|
|
functions.push_back(function_type(pushb));
|
|
typename base_type::rehash rehash;
|
|
functions.push_back(function_type(rehash));
|
|
typename base_type::swap swap;
|
|
populate p2(swap._M_other);
|
|
functions.push_back(function_type(swap));
|
|
typename base_type::assign_operator assignop;
|
|
populate p3(assignop._M_other);
|
|
functions.push_back(function_type(assignop));
|
|
#if __cplusplus >= 201103L
|
|
typename base_type::move_assign_operator massignop;
|
|
populate p4(massignop._M_other);
|
|
functions.push_back(function_type(massignop));
|
|
#endif
|
|
// Last.
|
|
typename base_type::clear clear;
|
|
functions.push_back(function_type(clear));
|
|
|
|
// Run tests.
|
|
size_t i(1);
|
|
for (auto it = functions.begin(); it != functions.end(); ++it)
|
|
{
|
|
function_type& f = *it;
|
|
i = run_steps_to_limit(i, container, f);
|
|
}
|
|
}
|
|
|
|
// Now that all instances has been destroyed check that there is no
|
|
// allocation remaining.
|
|
std::cout << "Checking remaining stuff" << std::endl;
|
|
__gnu_cxx::annotate_base::check();
|
|
}
|
|
|
|
template<typename _Funct>
|
|
size_t
|
|
run_steps_to_limit(size_t __step, container_type& __cont,
|
|
const _Funct& __f)
|
|
{
|
|
bool exit(false);
|
|
auto a = __cont.get_allocator();
|
|
|
|
do
|
|
{
|
|
// Use the current step as an allocator label.
|
|
a.set_label(__step);
|
|
|
|
try
|
|
{
|
|
condition_type::limit_adjustor limit(__step);
|
|
__f(__cont);
|
|
|
|
// If we get here, done.
|
|
exit = true;
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{
|
|
// Check this step for allocations.
|
|
// NB: Will throw std::logic_error if allocations.
|
|
a.check(__step);
|
|
|
|
// Check memory allocated with operator new.
|
|
|
|
}
|
|
++__step;
|
|
}
|
|
while (!exit);
|
|
|
|
// Log count info.
|
|
std::cout << __f.target_type().name() << std::endl;
|
|
std::cout << "end count " << __step << std::endl;
|
|
return __step;
|
|
}
|
|
};
|
|
|
|
|
|
// Run through all member functions with a no throw requirement, sudden death.
|
|
// all: member functions erase, pop_back, pop_front, swap
|
|
// iterator copy ctor, assignment operator
|
|
// unordered and associative: clear
|
|
// NB: Assumes _Tp::allocator_type is __gnu_cxx::throw_allocator_random.
|
|
template<typename _Tp>
|
|
struct generation_prohibited : public test_base<_Tp>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef test_base<container_type> base_type;
|
|
typedef typename base_type::populate populate;
|
|
typedef __gnu_cxx::random_condition condition_type;
|
|
|
|
generation_prohibited() { run(); }
|
|
|
|
void
|
|
run()
|
|
{
|
|
// Furthermore, assumes that the test functor will throw
|
|
// forced_exception via throw_allocator, that all errors are
|
|
// propagated and in error. Sudden death!
|
|
|
|
// Setup.
|
|
container_type container;
|
|
typename base_type::swap swap;
|
|
|
|
{
|
|
condition_type::never_adjustor off;
|
|
populate p1(container);
|
|
populate p2(swap._M_other);
|
|
}
|
|
|
|
// Run tests.
|
|
{
|
|
condition_type::always_adjustor on;
|
|
|
|
// NB: Vector and deque are special, erase can throw if the copy
|
|
// constructor or assignment operator of value_type throws.
|
|
if (!traits<container_type>::has_throwing_erase::value)
|
|
{
|
|
typename base_type::erase_point erasep;
|
|
erasep(container);
|
|
typename base_type::erase_range eraser;
|
|
eraser(container);
|
|
}
|
|
|
|
typename base_type::pop_front popf;
|
|
popf(container);
|
|
typename base_type::pop_back popb;
|
|
popb(container);
|
|
|
|
typename base_type::iterator_ops iops;
|
|
iops(container);
|
|
typename base_type::const_iterator_ops ciops;
|
|
ciops(container);
|
|
|
|
swap(container);
|
|
|
|
// Last.
|
|
typename base_type::clear clear;
|
|
clear(container);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
// Test strong exception guarantee.
|
|
// Run through all member functions with a roll-back, consistent
|
|
// coherent requirement.
|
|
// all: member functions insert and emplace of a single element, push_back,
|
|
// push_front
|
|
// unordered: rehash
|
|
template<typename _Tp>
|
|
struct propagation_consistent : public test_base<_Tp>
|
|
{
|
|
typedef _Tp container_type;
|
|
typedef test_base<container_type> base_type;
|
|
typedef typename base_type::populate populate;
|
|
typedef std::function<void(container_type&)> function_type;
|
|
typedef __gnu_cxx::limit_condition condition_type;
|
|
|
|
using base_type::compare;
|
|
|
|
propagation_consistent() { run(); }
|
|
|
|
// Run test.
|
|
void
|
|
run()
|
|
{
|
|
// Setup.
|
|
condition_type::never_adjustor off;
|
|
|
|
// Construct containers.
|
|
container_type container_control;
|
|
|
|
populate p(container_control);
|
|
|
|
// Construct list of member functions to exercise.
|
|
std::vector<function_type> functions;
|
|
typename base_type::emplace emplace;
|
|
functions.push_back(function_type(emplace));
|
|
typename base_type::emplace_point emplacep;
|
|
functions.push_back(function_type(emplacep));
|
|
typename base_type::emplace_front emplacef;
|
|
functions.push_back(function_type(emplacef));
|
|
typename base_type::emplace_back emplaceb;
|
|
functions.push_back(function_type(emplaceb));
|
|
typename base_type::push_front pushf;
|
|
functions.push_back(function_type(pushf));
|
|
typename base_type::push_back pushb;
|
|
functions.push_back(function_type(pushb));
|
|
typename base_type::insert_point insertp;
|
|
functions.push_back(function_type(insertp));
|
|
typename base_type::rehash rehash;
|
|
functions.push_back(function_type(rehash));
|
|
|
|
// Run tests.
|
|
for (auto i = functions.begin(); i != functions.end(); ++i)
|
|
{
|
|
function_type& f = *i;
|
|
run_steps_to_limit(container_control, f);
|
|
}
|
|
}
|
|
|
|
template<typename _Funct>
|
|
void
|
|
run_steps_to_limit(container_type& container_control, const _Funct& __f)
|
|
{
|
|
size_t i(1);
|
|
bool exit(false);
|
|
|
|
do
|
|
{
|
|
container_type container_test(container_control);
|
|
|
|
try
|
|
{
|
|
condition_type::limit_adjustor limit(i);
|
|
__f(container_test);
|
|
|
|
// If we get here, done.
|
|
exit = true;
|
|
}
|
|
catch(const __gnu_cxx::forced_error&)
|
|
{
|
|
compare(container_control, container_test);
|
|
++i;
|
|
}
|
|
}
|
|
while (!exit);
|
|
|
|
// Log count info.
|
|
std::cout << __f.target_type().name() << std::endl;
|
|
std::cout << "end count " << i << std::endl;
|
|
}
|
|
};
|
|
|
|
} // namespace __gnu_test
|
|
|
|
#endif
|