This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: Basic exception guarantee testcases (redux)
- From: Stephen M. Webb <stephen dot webb at bregmasoft dot com>
- To: Benjamin Kosnik <bkoz at redhat dot com>
- Cc: libstdc++ at gcc dot gnu dot org,gcc-patches at gcc dot gnu dot org
- Date: Mon, 25 Nov 2002 15:15:05 -0500
- Subject: Re: Basic exception guarantee testcases (redux)
- Organization: Bregmasoft
- References: <EXCHANGEByh133tYwli0000149f@pop.cryptocard.com> <EXCHANGE03a5sqMaSOR00001880@pop.cryptocard.com> <20021125124920.093cea9f.bkoz@redhat.com>
- Reply-to: stephen dot webb at bregmasoft dot com
On November 25, 2002 01:49 pm, Benjamin Kosnik wrote:
> Please send me a complete patch, with the new and old bits integrated,
> and I'll do this today.
ChangeLog
2002-11-25 Stephen M. Webb <stephen@bregmasoft.com>
* testsuite/testsuite_allocator.h: New file.
* testsuite/testsuite_allocator.cc: New file.
* testsuite/Makefile.am: Added testsuite_allocator to test library.
* testsuite/testsuite_hooks.h: Added more detailed tracker objects
gnu_copy_constructor, gnu_assignment_operator, and gnu_destructor.
* testsuite/testsuite_hooks.cc: Added new static objects definitions.
* testsuite/23_containers/vector_capacity.cc: Added reserve() tests.
* testsuite/23_containers/vector_ctor.cc: Added a plethora of tests.
* testsuite/23_containers/deque_ctor.cc: Added a slew of new tests.
Index: testsuite/testsuite_hooks.h
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/testsuite/testsuite_hooks.h,v
retrieving revision 1.10
diff -c -3 -p -r1.10 testsuite_hooks.h
*** testsuite/testsuite_hooks.h 2 Aug 2002 16:04:16 -0000 1.10
--- testsuite/testsuite_hooks.h 25 Nov 2002 19:14:21 -0000
*************** struct gnu_counting_struct
*** 100,156 ****
#define assert_count(n) VERIFY(gnu_counting_struct::count == n)
class gnu_copy_tracker
{
public:
! // Cannot be explicit. Conversion ctor used by list_modifiers.cc's
! // test03(), "range fill at beginning".
! gnu_copy_tracker (int anId, bool throwOnDemand = false)
! : itsId(anId), willThrow(throwOnDemand)
! {}
!
! gnu_copy_tracker (const gnu_copy_tracker& rhs)
! : itsId(rhs.id()), willThrow(rhs.willThrow)
{
- ++itsCopyCount;
- if (willThrow)
- __throw_exception_again "copy tracker exception";
}
! gnu_copy_tracker& operator=(const gnu_copy_tracker& rhs)
{
! itsId = rhs.id();
! // willThrow must obviously already be false to get this far
}
! ~gnu_copy_tracker() { ++itsDtorCount; }
int
id() const
! { return itsId; }
private:
! int itsId;
! const bool willThrow;
public:
static void
reset()
! { itsCopyCount = 0; itsDtorCount = 0; }
static int
copyCount()
! { return itsCopyCount; }
static int
dtorCount()
! { return itsDtorCount; }
private:
! static int itsCopyCount;
! static int itsDtorCount;
};
struct gnu_char
{
--- 100,273 ----
#define assert_count(n) VERIFY(gnu_counting_struct::count == n)
+ // A (static) class for counting copy constructors and possibly throwing an
+ // exception on a desired count.
+ class gnu_copy_constructor
+ {
+ public:
+ static unsigned int
+ count()
+ { return count_; }
+
+ static void
+ mark_call()
+ {
+ count_++;
+ if (count_ == throw_on_)
+ {
+ __throw_exception_again "copy constructor exception";
+ }
+ }
+
+ static void
+ reset()
+ {
+ count_ = 0;
+ throw_on_ = 0;
+ }
+
+ static void
+ throw_on(unsigned int count)
+ { throw_on_ = count; }
+
+ private:
+ static unsigned int count_;
+ static unsigned int throw_on_;
+ };
+
+ // A (static) class for counting assignment operator calls and possibly
+ // throwing an exception on a desired count.
+ class gnu_assignment_operator
+ {
+ public:
+ static unsigned int
+ count()
+ { return count_; }
+
+ static void
+ mark_call()
+ {
+ count_++;
+ if (count_ == throw_on_)
+ {
+ __throw_exception_again "assignment operator exception";
+ }
+ }
+
+ static void
+ reset()
+ {
+ count_ = 0;
+ throw_on_ = 0;
+ }
+
+ static void
+ throw_on(unsigned int count)
+ { throw_on_ = count; }
+
+ private:
+ static unsigned int count_;
+ static unsigned int throw_on_;
+ };
+ // A (static) class for tracking calls to an object's destructor.
+ class gnu_destructor
+ {
+ public:
+ static unsigned int
+ count()
+ { return count_; }
+
+ static void
+ mark_call()
+ { count_++; }
+
+ static void
+ reset()
+ { count_ = 0; }
+
+ private:
+ static unsigned int count_;
+ };
+
+ // An class of objects that can be used for validating various behaviours
and
+ // guarantees of containers and algorithms defined in the standard library.
class gnu_copy_tracker
{
public:
! // Creates a copy-tracking object with the given ID number.
! // If "throw_on_copy" is set, an exception will be thrown if
! // an attempt is made to copy this object.
! gnu_copy_tracker(int id = next_id_--, bool throw_on_copy = false)
! : id_(id)
! , throw_on_copy_(throw_on_copy)
{
}
! // Copy-constructs the object, marking a call to the copy
! // constructor and forcing an exception if indicated.
! gnu_copy_tracker(const gnu_copy_tracker& rhs)
! : id_(rhs.id()), throw_on_copy_(rhs.throw_on_copy_)
{
! int kkk = throw_on_copy_;
! if (throw_on_copy_)
! {
! gnu_copy_constructor::throw_on(gnu_copy_constructor::count() + 1);
! }
! gnu_copy_constructor::mark_call();
}
! // Assigns the value of another object to this one, tracking the number
of
! // times this member function has been called and if the other object is
! // supposed to throw an exception when it is copied, well, make it so.
! gnu_copy_tracker&
! operator=(const gnu_copy_tracker& rhs)
! {
! id_ = rhs.id();
! if (rhs.throw_on_copy_)
! {
! gnu_assignment_operator::throw_on(gnu_assignment_operator::count()
+ 1);
! }
! gnu_assignment_operator::mark_call();
! }
!
! ~gnu_copy_tracker()
! { gnu_destructor::mark_call(); }
int
id() const
! { return id_; }
private:
! int id_;
! const bool throw_on_copy_;
public:
static void
reset()
! {
! gnu_copy_constructor::reset();
! gnu_assignment_operator::reset();
! gnu_destructor::reset();
! }
+ // for backwards-compatibility
static int
copyCount()
! { return gnu_copy_constructor::count(); }
+ // for backwards-compatibility
static int
dtorCount()
! { return gnu_destructor::count(); }
private:
! static int next_id_;
};
+
+ inline bool
+ operator==(const gnu_copy_tracker& lhs, const gnu_copy_tracker& rhs)
+ { return lhs.id() == rhs.id(); }
struct gnu_char
{
Index: testsuite/testsuite_hooks.cc
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/testsuite/testsuite_hooks.cc,v
retrieving revision 1.4
diff -c -3 -p -r1.4 testsuite_hooks.cc
*** testsuite/testsuite_hooks.cc 24 Oct 2002 23:27:27 -0000 1.4
--- testsuite/testsuite_hooks.cc 25 Nov 2002 19:14:21 -0000
*************** __set_testsuite_memlimit(float) { }
*** 78,83 ****
gnu_counting_struct::size_type gnu_counting_struct::count = 0;
! int gnu_copy_tracker::itsCopyCount = 0;
! int gnu_copy_tracker::itsDtorCount = 0;
--- 78,87 ----
gnu_counting_struct::size_type gnu_counting_struct::count = 0;
! unsigned int gnu_copy_constructor::count_ = 0;
! unsigned int gnu_copy_constructor::throw_on_ = 0;
! unsigned int gnu_assignment_operator::count_ = 0;
! unsigned int gnu_assignment_operator::throw_on_ = 0;
! unsigned int gnu_destructor::count_ = 0;
! int gnu_copy_tracker::next_id_ = 0;
Index: testsuite/Makefile.am
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/testsuite/Makefile.am,v
retrieving revision 1.11
diff -c -3 -p -r1.11 Makefile.am
*** testsuite/Makefile.am 1 Sep 2002 18:09:18 -0000 1.11
--- testsuite/Makefile.am 25 Nov 2002 19:14:21 -0000
*************** INCLUDES = \
*** 51,57 ****
## Build support library.
noinst_LIBRARIES = libv3test.a
! libv3test_a_SOURCES = testsuite_hooks.cc
## Build support utilities.
## Only build this as native, as need to find startup files and libc to
link.
--- 51,57 ----
## Build support library.
noinst_LIBRARIES = libv3test.a
! libv3test_a_SOURCES = testsuite_hooks.cc testsuite_allocator.cc
## Build support utilities.
## Only build this as native, as need to find startup files and libc to
link.
Index: testsuite/23_containers/vector_capacity.cc
===================================================================
RCS file:
/cvs/gcc/gcc/libstdc++-v3/testsuite/23_containers/vector_capacity.cc,v
retrieving revision 1.5
diff -c -3 -p -r1.5 vector_capacity.cc
*** testsuite/23_containers/vector_capacity.cc 16 Nov 2002 17:16:31 -0000 1.5
--- testsuite/23_containers/vector_capacity.cc 25 Nov 2002 19:14:21 -0000
***************
*** 23,28 ****
--- 23,29 ----
#include <vector>
#include <stdexcept>
+ #include <testsuite_allocator.h>
#include <testsuite_hooks.h>
template<typename T>
*************** void test03()
*** 119,128 ****
--- 120,190 ----
VERIFY( test );
}
+ // Verifies basic functionality of reserve() with forced reallocation.
+ void
+ test_reserve()
+ {
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(3);
+ const X::size_type old_size = a.size();
+ const X::size_type old_capacity = a.capacity();
+ const X::size_type new_capacity = old_capacity + 10;
+ T::reset();
+
+ a.reserve(new_capacity);
+
+ // [23.2.4.1 (2)]
+ VERIFY(new_capacity <= a.capacity());
+ // [23.2.4.1 (3)]
+ VERIFY(old_size == a.size());
+ VERIFY(gnu_copy_constructor::count() <= old_size);
+ VERIFY(gnu_destructor::count() <= old_size);
+ }
+ // check for memory leaks
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+ }
+
+ // Verifies that reserve() with reallocation offers the strong exception
guarantee.
+ void
+ test_reserve_exception_guarantee()
+ {
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(7);
+ const X::size_type old_size = a.size();
+ const X::size_type old_capacity = a.capacity();
+ const X::size_type new_capacity = old_capacity + 10;
+ T::reset();
+ gnu_copy_constructor::throw_on(3);
+
+ try
+ {
+ a.reserve(new_capacity);
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+
+ VERIFY(old_capacity == a.capacity());
+ VERIFY(gnu_copy_constructor::count() == gnu_destructor::count()+1);
+ }
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+ }
+
int main()
{
test01();
test02();
test03();
+ test_reserve();
+ test_reserve_exception_guarantee();
return 0;
}
Index: testsuite/23_containers/vector_ctor.cc
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/testsuite/23_containers/vector_ctor.cc,v
retrieving revision 1.8
diff -c -3 -p -r1.8 vector_ctor.cc
*** testsuite/23_containers/vector_ctor.cc 1 May 2002 02:17:35 -0000 1.8
--- testsuite/23_containers/vector_ctor.cc 25 Nov 2002 19:14:22 -0000
***************
*** 22,27 ****
--- 22,28 ----
#include <vector>
#include <string>
+ #include <testsuite_allocator.h>
#include <testsuite_hooks.h>
template<typename T>
*************** void test04()
*** 94,105 ****
--- 95,641 ----
#endif
}
+
+ // @fn test_default_ctor_exception_gurantee
+ // This test verifies that if one of the vector's contained objects throws
+ // an exception from its constructor while the vector is being constructed
and
+ // filled with default values, all memory is returned to the allocator
whence
+ // it came.
+ void
+ test_default_ctor_exception_gurantee()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_copy_tracker::reset();
+ gnu_copy_constructor::throw_on(3);
+ gnu_allocator_tracker::resetCounts();
+
+ // run test
+ try
+ {
+ X a(7);
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+
+ // assert postconditions
+ VERIFY(("memory leak detected:",
+ gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal()));
+
+ // teardown
+ }
+
+ // @fn test_copy_ctor_exception_gurantee
+ // This test verifies that if one of the vector's contained objects throws
+ // an exception from its constructor while the vector is being copy
+ // constructed, all memory is returned to the allocator whence it came.
+ void
+ test_copy_ctor_exception_gurantee()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(7);
+ gnu_copy_tracker::reset();
+ gnu_copy_constructor::throw_on(3);
+
+ // run test
+ try
+ {
+ X u(a);
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+ }
+
+ // assert postconditions
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ // operator=()
+ //
+ // case 1: lhs.size() > rhs.size()
+ // case 2: lhs.size() < rhs.size() < lhs.capacity()
+ // case 3: lhs.capacity() < rhs.size()
+ //
+ void
+ test_assignment_operator_1()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ X r(9);
+ X a(r.size() - 2);
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+
+ // preconditions
+ VERIFY(r.size() > a.size());
+
+ // run test
+ r = a;
+
+ // assert postconditions
+ VERIFY(r == a);
+ VERIFY(gnu_allocator_tracker::allocationTotal() == 0);
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_assignment_operator_2()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ X r(1);
+ r.reserve(17);
+ X a(r.size() + 7);
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+
+ // preconditions
+ VERIFY(r.size() < a.size());
+ VERIFY(a.size() < r.capacity());
+
+ // run test
+ r = a;
+
+ // assert postconditions
+ VERIFY(r == a);
+ VERIFY(gnu_allocator_tracker::allocationTotal() == 0);
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_assignment_operator_3()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X r(1);
+ X a(r.capacity() + 7);
+ gnu_copy_tracker::reset();
+
+ // preconditions
+ VERIFY(r.capacity() < a.size());
+
+ // run test
+ r = a;
+
+ // assert postconditions
+ VERIFY(r == a);
+ }
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_assignment_operator_3_exception_guarantee()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X r(1);
+ X a(r.capacity() + 7);
+ gnu_copy_tracker::reset();
+ gnu_copy_constructor::throw_on(3);
+
+ // preconditions
+ VERIFY(r.capacity() < a.size());
+
+ // run test
+ try
+ {
+ r = a;
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+ }
+
+ // assert postconditions
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ // fill assign()
+ //
+ // case 1: [23.2.4.1 (3)] n <= size()
+ // case 2: [23.2.4.1 (3)] size() < n <= capacity()
+ // case 3: [23.2.4.1 (3)] n > capacity()
+ // case 4: [23.2.4.1 (3)] n > capacity(), exception guarantees
+ // case 5: [23.1.1 (9)] fill assign disguised as a range assign
+ //
+ void
+ test_fill_assign_1()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ X a(7);
+ X::size_type old_size = a.size();
+ X::size_type new_size = old_size - 2;
+ const T t;
+
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+
+ // run test
+ a.assign(new_size, t);
+
+ // assert postconditions
+ VERIFY(a.size() == new_size);
+ VERIFY(gnu_allocator_tracker::allocationTotal() == 0);
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_fill_assign_2()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ X a(7);
+ a.reserve(11);
+ X::size_type old_size = a.size();
+ X::size_type old_capacity = a.capacity();
+ X::size_type new_size = old_size + 2;
+ const T t;
+
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+
+ // assert preconditions
+ VERIFY(old_size < new_size);
+ VERIFY(new_size <= old_capacity);
+
+ // run test
+ a.assign(new_size, t);
+
+ // assert postconditions
+ VERIFY(a.size() == new_size);
+ VERIFY(gnu_allocator_tracker::allocationTotal() == 0);
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_fill_assign_3()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(7);
+ X::size_type old_size = a.size();
+ X::size_type old_capacity = a.capacity();
+ X::size_type new_size = old_capacity + 4;
+ const T t;
+
+ gnu_copy_tracker::reset();
+
+ // assert preconditions
+ VERIFY(new_size > old_capacity);
+
+ // run test
+ a.assign(new_size, t);
+
+ // assert postconditions
+ VERIFY(a.size() == new_size);
+ }
+
+ VERIFY(gnu_allocator_tracker::allocationTotal() > 0);
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_fill_assign_3_exception_guarantee()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(7);
+ X::size_type old_size = a.size();
+ X::size_type old_capacity = a.capacity();
+ X::size_type new_size = old_capacity + 4;
+ const T t;
+
+ gnu_copy_tracker::reset();
+ gnu_copy_constructor::throw_on(3);
+
+ // assert preconditions
+ VERIFY(new_size > old_capacity);
+
+ // run test
+ try
+ {
+ a.assign(new_size, t);
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+
+ // assert postconditions
+ VERIFY(a.size() == old_size);
+ VERIFY(a.capacity() == old_capacity);
+ }
+
+ VERIFY(gnu_allocator_tracker::allocationTotal() > 0);
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_fill_assign_4()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ X a(7);
+ X::size_type old_size = a.size();
+ X::size_type new_size = old_size - 2;
+ X::size_type new_value = 117;
+
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+
+ // run test
+ a.assign(new_size, new_value);
+
+ // assert postconditions
+ VERIFY(a.size() == new_size);
+ VERIFY(gnu_allocator_tracker::allocationTotal() == 0);
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ // range assign()
+ //
+ // case 1: [23.2.4.1 (2)] input iterator
+ // case 2: [23.2.4.1 (2)] forward iterator, distance(first, last) <= size()
+ // case 3: [23.2.4.1 (2)] forward iterator, size() < distance(first, last)
<= capacity()
+ // case 4: [23.2.4.1 (2)] forward iterator, distance(first, last) >
capacity()
+ // case 5: [23.2.4.1 (2)] forward iterator, distance(first, last) >
capacity(), exception guarantees
+ void
+ test_range_assign_1()
+ {
+ // @TODO
+ }
+
+ void
+ test_range_assign_2()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ X a(7);
+ X b(3);
+ X::size_type old_size = a.size();
+
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+
+ // assert preconditions
+ VERIFY(b.size() < a.capacity());
+
+ // run test
+ a.assign(b.begin(), b.end());
+
+ // assert postconditions
+ VERIFY(a.size() == b.size());
+ VERIFY(a == b);
+ VERIFY(gnu_allocator_tracker::allocationTotal() == 0);
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_range_assign_3()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ X a(7);
+ a.reserve(a.size() + 7);
+ X b(a.size() + 3);
+ X::size_type old_size = a.size();
+
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+
+ // assert preconditions
+ VERIFY(a.size() < b.size());
+ VERIFY(b.size() < a.capacity());
+
+ // run test
+ a.assign(b.begin(), b.end());
+
+ // assert postconditions
+ VERIFY(a.size() == b.size());
+ VERIFY(a == b);
+ VERIFY(gnu_allocator_tracker::allocationTotal() == 0);
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_range_assign_4()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(7);
+ X b(a.capacity() + 7);
+ X::size_type old_size = a.size();
+
+ gnu_copy_tracker::reset();
+
+ // assert preconditions
+ VERIFY(b.size() > a.capacity());
+
+ // run test
+ a.assign(b.begin(), b.end());
+
+ // assert postconditions
+ VERIFY(a.size() == b.size());
+ VERIFY(a == b);
+ }
+ VERIFY(gnu_allocator_tracker::allocationTotal() > 0);
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+ void
+ test_range_assign_4_exception_guarantee()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::vector<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(7);
+ X b(a.capacity() + 7);
+ X::size_type old_size = a.size();
+
+ gnu_copy_tracker::reset();
+ gnu_copy_constructor::throw_on(3);
+
+ // assert preconditions
+ VERIFY(b.size() > a.capacity());
+
+ // run test
+ try
+ {
+ a.assign(b.begin(), b.end());
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+ }
+
+ // assert postconditions
+ VERIFY(gnu_allocator_tracker::allocationTotal() > 0);
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ gnu_copy_tracker::reset();
+ gnu_allocator_tracker::resetCounts();
+ }
+
+
int main()
{
test01();
test02();
test03();
test04();
+ test_default_ctor_exception_gurantee();
+ test_copy_ctor_exception_gurantee();
+ test_assignment_operator_1();
+ test_assignment_operator_2();
+ test_assignment_operator_3();
+ test_assignment_operator_3_exception_guarantee();
+ test_fill_assign_1();
+ test_fill_assign_2();
+ test_fill_assign_3();
+ test_fill_assign_3_exception_guarantee();
+ test_fill_assign_4();
+ test_range_assign_1();
+ test_range_assign_2();
+ test_range_assign_3();
+ test_range_assign_4();
+ test_range_assign_4_exception_guarantee();
return 0;
}
Index: testsuite/23_containers/deque_ctor.cc
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/testsuite/23_containers/deque_ctor.cc,v
retrieving revision 1.1
diff -c -3 -p -r1.1 deque_ctor.cc
*** testsuite/23_containers/deque_ctor.cc 27 Dec 2001 21:51:28 -0000 1.1
--- testsuite/23_containers/deque_ctor.cc 25 Nov 2002 19:57:18 -0000
***************
*** 21,32 ****
// 23.2.1.1 deque constructors, copy, and assignment
#include <deque>
#include <testsuite_hooks.h>
typedef std::deque<gnu_counting_struct> gdeque;
! // basic alloc/dealloc sanity check
void
test01()
{
--- 21,36 ----
// 23.2.1.1 deque constructors, copy, and assignment
#include <deque>
+ #include <iterator>
+ #include <sstream>
+ #include <testsuite_allocator.h>
#include <testsuite_hooks.h>
typedef std::deque<gnu_counting_struct> gdeque;
+ bool test = true;
! // see http://gcc.gnu.org/ml/libstdc++/2001-11/msg00139.html
void
test01()
{
*************** test01()
*** 38,46 ****
assert_count (0);
}
int main()
{
test01();
! return 0;
}
--- 42,531 ----
assert_count (0);
}
+
+ // 23.2.1 required types
+ //
+ // A missing required type will cause a compile failure.
+ //
+ void
+ requiredTypesCheck()
+ {
+ typedef int T;
+ typedef std::deque<T> X;
+
+ typedef X::reference reference;
+ typedef X::const_reference const_reference;
+ typedef X::iterator iterator;
+ typedef X::const_iterator const_iterator;
+ typedef X::size_type size_type;
+ typedef X::difference_type difference_type;
+ typedef X::value_type value_type;
+ typedef X::allocator_type allocator_type;
+ typedef X::pointer pointer;
+ typedef X::const_pointer const_pointer;
+ typedef X::reverse_iterator reverse_iterator;
+ typedef X::const_reverse_iterator const_reverse_iterator;
+ }
+
+
+ // @fn defaultConstructorCheck
+ // Explicitly checks the default deque constructor and destructor for both
+ // trivial and non-trivial types. In addition, the size() and empty()
+ // member functions are explicitly checked here since it should be their
+ // first use. Checking those functions means checking the begin() and
+ // end() and their const brethren functions as well.
+ //
+ // @verbatim
+ // 23.2.1.1 default ctor/dtor
+ // effects:
+ // 23.2.1.1 constructs an empty deque using the specified
allocator
+ // postconditions:
+ // 23.1 table 65 u.size() == 0
+ // throws:
+ // complexity:
+ // 23.1 table 65 constant
+ //
+ // 23.2.1.2 bool empty() const
+ // semantics:
+ // 23.1 table 65 a.size() == 0
+ // 23.1 (7) a.begin() == a.end()
+ // throws:
+ // complexity:
+ // 23.1 table 65 constant
+ //
+ // 23.2.1.2 size_type size() const
+ // semantics:
+ // 23.1 table 65 a.end() - a.begin()
+ // throws:
+ // complexity:
+ // 23.1 table 65(A) should be constant
+ //
+ // 23.2.1 iterator begin()
+ // const_iterator begin() const
+ // iterator end()
+ // const_iterator end() const
+ // throws:
+ // 23.1 (10) pt. 4 does not throw
+ // complexity:
+ // 23.1 table 65 constant
+ // @endverbatim
+ void
+ defaultConstructorCheckPOD()
+ {
+ // setup
+ typedef int T;
+ typedef std::deque<T> X;
+
+ // run test
+ X u;
+
+ // assert postconditions
+ VERIFY(u.empty());
+ VERIFY(0 == u.size());
+ VERIFY(u.begin() == u.end());
+ VERIFY(0 == std::distance(u.begin(), u.end()));
+
+ // teardown
+ }
+
+
+ void
+ defaultConstructorCheck()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ gnu_copy_tracker::reset();
+
+ // run test
+ const X u;
+
+ // assert postconditions
+ VERIFY(u.empty());
+ VERIFY(0 == u.size());
+ VERIFY(u.begin() == u.end());
+ VERIFY(0 == std::distance(u.begin(), u.end()));
+
+ // teardown
+ }
+
+
+ // @fn copyConstructorCheck()
+ // Explicitly checks the deque copy constructor. Continues verificaton of
+ // ancillary member functions documented under defaultConstructorCheck().
+ //
+ // This check also tests the push_back() member function.
+ //
+ // @verbatim
+ // 23.2.1 copy constructor
+ // effects:
+ // postconditions:
+ // 22.1.1 table 65 a == X(a)
+ // u == a
+ // throws:
+ // complexity:
+ // 22.1.1 table 65 linear
+ // @endverbatim
+ void
+ copyConstructorCheck()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ const int copyBaseSize = 17; // arbitrary
+
+ X a;
+ for (int i = 0; i < copyBaseSize; ++i)
+ a.push_back(i);
+ gnu_copy_tracker::reset();
+
+ // assert preconditions
+ VERIFY(!a.empty());
+ VERIFY(copyBaseSize == a.size());
+ VERIFY(a.begin() != a.end());
+ VERIFY(copyBaseSize == std::distance(a.begin(), a.end()));
+
+ // run test
+ X u = a;
+
+ // assert postconditions
+ VERIFY(u == a);
+ VERIFY(copyBaseSize == gnu_copy_constructor::count());
+
+ // teardown
+ }
+
+
+ // @fn fillConstructorCheck()
+ // This test explicitly verifies the basic fill constructor. Like the
default
+ // constructor, later tests depend on the fill constructor working
correctly.
+ // That means this explicit test should preceed the later tests so the error
+ // message given on assertion failure can be more helpful n tracking the
+ // problem.
+ //
+ // 23.2.1.1 fill constructor
+ // complexity:
+ // 23.2.1.1 linear in N
+ void
+ fillConstructorCheck()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ const X::size_type n(23);
+ const X::value_type t(111);
+
+ gnu_copy_tracker::reset();
+
+ // run test
+ X a(n, t);
+
+ // assert postconditions
+ VERIFY(n == a.size());
+ VERIFY(n == gnu_copy_constructor::count());
+
+ // teardown
+ }
+
+
+ // @fn fillConstructorCheck2()
+ // Explicit check for fill constructors masqueraded as range constructors as
+ // elucidated in clause 23.1.1 paragraph 9 of the standard.
+ //
+ // 23.1.1 (9) fill constructor looking like a range constructor
+ void
+ fillConstructorCheck2()
+ {
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ const int f = 23;
+ const int l = 111;
+
+ gnu_copy_tracker::reset();
+
+ X a(f, l);
+
+ VERIFY(f == a.size());
+ VERIFY(f == gnu_copy_constructor::count());
+ }
+
+
+ // @fn rangeConstructorCheckForwardIterator()
+ // This test copies from one deque to another to force the copy
+ // constructor for T to be used because the compiler will kindly
+ // elide copies if the default constructor can be used with
+ // type conversions. Trust me.
+ //
+ // 23.2.1.1 range constructor, forward iterators
+ void
+ rangeConstructorCheckForwardIterator()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ const X::size_type n(726);
+ const X::value_type t(307);
+ X source(n, t);
+ X::iterator i = source.begin();
+ X::iterator j = source.end();
+ X::size_type rangeSize = std::distance(i, j);
+
+ gnu_copy_tracker::reset();
+
+ // test
+ X a(i, j);
+
+ // assert postconditions
+ VERIFY(rangeSize == a.size());
+ VERIFY(gnu_copy_constructor::count() <= rangeSize);
+ }
+
+
+ // @fn rangeConstructorCheckInputIterator()
+ // An explicit check for range construction on an input iterator
+ // range, which the standard expounds upon as having a different
+ // complexity than forward iterators.
+ //
+ // 23.2.1.1 range constructor, input iterators
+ void
+ rangeConstructorCheckInputIterator()
+ {
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ std::istringstream ibuf("1234567890123456789");
+ const X::size_type rangeSize = ibuf.str().size();
+ std::istream_iterator<char> i(ibuf);
+ std::istream_iterator<char> j;
+
+ gnu_copy_tracker::reset();
+
+ X a(i, j);
+
+ VERIFY(rangeSize == a.size());
+ VERIFY(gnu_copy_constructor::count() <= (2 * rangeSize));
+ }
+
+
+ // 23.2.1 copy assignment
+ void
+ copyAssignmentCheck()
+ {
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ const X::size_type n(18);
+ const X::value_type t(1023);
+ X a(n, t);
+ X r;
+
+ gnu_copy_tracker::reset();
+
+ r = a;
+
+ VERIFY(r == a);
+ VERIFY(n == gnu_copy_constructor::count());
+ }
+
+
+ // 23.2.1.1 fill assignment
+ //
+ // The complexity check must check dtors+copyAssign and copyCtor+copyAssign
+ // because that's the way the SGI implementation works. Dunno if it's true
+ // standard compliant (which specifies fill assignment in terms of erase and
+ // insert only), but it should work as (most) users expect and is more
efficient.
+ void
+ fillAssignmentCheck()
+ {
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ const X::size_type starting_size(10);
+ const X::value_type starting_value(66);
+ const X::size_type n(23);
+ const X::value_type t(111);
+
+ X a(starting_size, starting_value);
+ gnu_copy_tracker::reset();
+
+ // preconditions
+ VERIFY(starting_size == a.size());
+
+ // test
+ a.assign(n, t);
+
+ // postconditions
+ VERIFY(n == a.size());
+ VERIFY(n == (gnu_copy_constructor::count() +
gnu_assignment_operator::count()));
+ VERIFY(starting_size == (gnu_destructor::count() +
gnu_assignment_operator::count()));
+ }
+
+
+ // @verbatim
+ // 23.2.1 range assignment
+ // 23.2.1.1 deque constructors, copy, and assignment
+ // effects:
+ // Constructs a deque equal to the range [first, last), using the
specified allocator.
+ //
+ // template<typename InputIterator>
+ // assign(InputIterator first, InputIterator last);
+ //
+ // is equivalent to
+ //
+ // erase(begin(), end());
+ // insert(begin(), first, last);
+ //
+ // postconditions:
+ // throws:
+ // complexity:
+ // forward iterators: N calls to the copy constructor, 0 reallocations
+ // input iterators: 2N calls to the copy constructor, log(N)
reallocations
+ // @endverbatim
+ void
+ rangeAssignmentCheck()
+ {
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ const X::size_type source_size(726);
+ const X::value_type source_value(307);
+ const X::size_type starting_size(10);
+ const X::value_type starting_value(66);
+
+ X source(source_size, source_value);
+ X::iterator i = source.begin();
+ X::iterator j = source.end();
+ X::size_type rangeSize = std::distance(i, j);
+
+ X a(starting_size, starting_value);
+ VERIFY(starting_size == a.size());
+
+ gnu_copy_tracker::reset();
+
+ a.assign(i, j);
+
+ VERIFY(source == a);
+ VERIFY(rangeSize == (gnu_copy_constructor::count() +
gnu_assignment_operator::count()));
+ VERIFY(starting_size == (gnu_destructor::count() +
gnu_assignment_operator::count()));
+ }
+
+
+ // 23.1 (10) range assignment
+ // 23.2.1.3 with exception
+ void
+ rangeAssignmentCheckWithException()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ // test
+ // What does "no effects" mean?
+ }
+
+
+ // 23.1.1 (9) fill assignment looking like a range assignment
+ void
+ fillAssignmentCheck2()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T> X;
+
+ // test
+ // What does "no effects" mean?
+ }
+
+ // Verify that the default deque constructor offers the basic exception
+ // guarantee.
+ void
+ test_default_ctor_exception_safety()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T, gnu_new_allocator<T> > X;
+
+ T::reset();
+ gnu_copy_constructor::throw_on(3);
+ gnu_allocator_tracker::resetCounts();
+
+ // test
+ try
+ {
+ X a(7);
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+
+ // assert postconditions
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ }
+
+ // Verify that the copy constructor offers the basic exception guarantee.
+ void
+ test_copy_ctor_exception_safety()
+ {
+ // setup
+ typedef gnu_copy_tracker T;
+ typedef std::deque<T, gnu_new_allocator<T> > X;
+
+ gnu_allocator_tracker::resetCounts();
+ {
+ X a(7);
+ T::reset();
+ gnu_copy_constructor::throw_on(3);
+
+
+ // test
+ try
+ {
+ X u(a);
+ VERIFY(("no exception thrown", false));
+ }
+ catch (...)
+ {
+ }
+ }
+
+ // assert postconditions
+ VERIFY(gnu_allocator_tracker::allocationTotal() ==
gnu_allocator_tracker::deallocationTotal());
+
+ // teardown
+ }
+
+
int main()
{
+ // basic functionality and standard conformance checks
+ requiredTypesCheck();
+ defaultConstructorCheckPOD();
+ defaultConstructorCheck();
+ test_default_ctor_exception_safety();
+ copyConstructorCheck();
+ test_copy_ctor_exception_safety();
+ fillConstructorCheck();
+ fillConstructorCheck2();
+ rangeConstructorCheckInputIterator();
+ rangeConstructorCheckForwardIterator();
+ copyAssignmentCheck();
+ fillAssignmentCheck();
+ fillAssignmentCheck2();
+ rangeAssignmentCheck();
+ rangeAssignmentCheckWithException();
+
+ // specific bug fix checks
test01();
! return !test;
}
--
Stephen M. Webb
// Testing allocator for the C++ library testsuite.
//
// Copyright (C) 2002 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 2, 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 COPYING. If not, write to the Free
// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
// This file provides an test instrumentation allocator that can be used to
// verify allocation functionality of standard library containers.
// 2002.11.25 smw
#ifndef _GLIBCPP_TESTSUITE_ALLOCATOR_H
#define _GLIBCPP_TESTSUITE_ALLOCATOR_H
#include <cstddef>
#include <limits>
class gnu_allocator_tracker
{
public:
typedef std::size_t size_type;
static void*
allocate(size_type blocksize)
{
allocationTotal_ += blocksize;
return ::operator new(blocksize);
}
static void
construct()
{ constructCount_++; }
static void
destroy()
{ destructCount_++; }
static void
deallocate(void* p, size_type blocksize)
{
::operator delete(p);
deallocationTotal_ += blocksize;
}
static size_type
allocationTotal()
{ return allocationTotal_; }
static size_type
deallocationTotal()
{ return deallocationTotal_; }
static int
constructCount()
{ return constructCount_; }
static int
destructCount()
{ return destructCount_; }
static void
resetCounts()
{
allocationTotal_ = 0;
deallocationTotal_ = 0;
constructCount_ = 0;
destructCount_ = 0;
}
private:
static size_type allocationTotal_;
static size_type deallocationTotal_;
static int constructCount_;
static int destructCount_;
};
// A simple basic allocator that just forwards to the gnu_allocator_tracker
// to fulfill memory requests. This class is templated on the target object
// type, but gnu_allocator_tracker isn't.
template <class T>
class gnu_new_allocator
{
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template <class U> struct rebind { typedef gnu_new_allocator<U> other; };
pointer
address(reference value) const
{ return &value; }
const_pointer
address(const_reference value) const
{ return &value; }
gnu_new_allocator() throw()
{ }
gnu_new_allocator(const gnu_new_allocator&) throw()
{ }
template <class U>
gnu_new_allocator(const gnu_new_allocator<U>&) throw()
{ }
~gnu_new_allocator() throw()
{ }
size_type
max_size() const throw()
{ return std::numeric_limits<std::size_t>::max() / sizeof(T); }
pointer
allocate(size_type num, const void* = 0)
{ return static_cast<pointer>(gnu_allocator_tracker::allocate(num * sizeof(T))); }
void
construct(pointer p, const T& value)
{
new (p) T(value);
gnu_allocator_tracker::construct();
}
void
destroy(pointer p)
{
p->~T();
gnu_allocator_tracker::destroy();
}
void
deallocate(pointer p, size_type num)
{ gnu_allocator_tracker::deallocate(p, num * sizeof(T)); }
};
template <class T1, class T2>
bool
operator==(const gnu_new_allocator<T1>&, const gnu_new_allocator<T2>&) throw()
{ return true; }
template <class T1, class T2>
bool
operator!=(const gnu_new_allocator<T1>&, const gnu_new_allocator<T2>&) throw()
{ return false; }
#endif // _GLIBCPP_TESTSUITE_ALLOCATOR_H
//
// Copyright (C) 2002 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 2, 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 COPYING. If not, write to the Free
// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
#include <testsuite_allocator.h>
gnu_allocator_tracker::size_type gnu_allocator_tracker::allocationTotal_ = 0;
gnu_allocator_tracker::size_type gnu_allocator_tracker::deallocationTotal_ = 0;
int gnu_allocator_tracker::constructCount_ = 0;
int gnu_allocator_tracker::destructCount_ = 0;