Commit 162c7cd9 by Stephen M. Webb Committed by Benjamin Kosnik

testsuite_allocator.h: New file.


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.

From-SVN: r59484
parent 83bbd9b6
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.
2002-11-25 Mark Mitchell <mark@codesourcery.com> 2002-11-25 Mark Mitchell <mark@codesourcery.com>
* libsupc++/Makefile.am (sources): Add guard.cc. * libsupc++/Makefile.am (sources): Add guard.cc.
......
dnl aclocal.m4 generated automatically by aclocal 1.4-p6 dnl aclocal.m4 generated automatically by aclocal 1.4-p5
dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation dnl This file is free software; the Free Software Foundation
...@@ -2262,7 +2262,7 @@ AC_MSG_RESULT($enable_symvers) ...@@ -2262,7 +2262,7 @@ AC_MSG_RESULT($enable_symvers)
]) ])
# isc-posix.m4 serial 2 (gettext-0.11.2) # isc-posix.m4 serial 1 (gettext-0.10.40)
dnl Copyright (C) 1995-2002 Free Software Foundation, Inc. dnl Copyright (C) 1995-2002 Free Software Foundation, Inc.
dnl This file is free software, distributed under the terms of the GNU dnl This file is free software, distributed under the terms of the GNU
dnl General Public License. As a special exception to the GNU General dnl General Public License. As a special exception to the GNU General
...@@ -2270,8 +2270,6 @@ dnl Public License, this file may be distributed as part of a program ...@@ -2270,8 +2270,6 @@ dnl Public License, this file may be distributed as part of a program
dnl that contains a configuration script generated by Autoconf, under dnl that contains a configuration script generated by Autoconf, under
dnl the same distribution terms as the rest of that program. dnl the same distribution terms as the rest of that program.
# This file is not needed with autoconf-2.53 and newer. Remove it in 2005.
# This test replaces the one in autoconf. # This test replaces the one in autoconf.
# Currently this macro should have the same name as the autoconf macro # Currently this macro should have the same name as the autoconf macro
# because gettext's gettext.m4 (distributed in the automake package) # because gettext's gettext.m4 (distributed in the automake package)
...@@ -2332,8 +2330,7 @@ dnl Usage: ...@@ -2332,8 +2330,7 @@ dnl Usage:
dnl AM_INIT_AUTOMAKE(package,version, [no-define]) dnl AM_INIT_AUTOMAKE(package,version, [no-define])
AC_DEFUN([AM_INIT_AUTOMAKE], AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl [AC_REQUIRE([AC_PROG_INSTALL])
AC_REQUIRE([AC_PROG_INSTALL])
PACKAGE=[$1] PACKAGE=[$1]
AC_SUBST(PACKAGE) AC_SUBST(PACKAGE)
VERSION=[$2] VERSION=[$2]
...@@ -2349,42 +2346,13 @@ AC_REQUIRE([AM_SANITY_CHECK]) ...@@ -2349,42 +2346,13 @@ AC_REQUIRE([AM_SANITY_CHECK])
AC_REQUIRE([AC_ARG_PROGRAM]) AC_REQUIRE([AC_ARG_PROGRAM])
dnl FIXME This is truly gross. dnl FIXME This is truly gross.
missing_dir=`cd $ac_aux_dir && pwd` missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}, $missing_dir) AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}, $missing_dir) AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AC_REQUIRE([AC_PROG_MAKE_SET])]) AC_REQUIRE([AC_PROG_MAKE_SET])])
# Copyright 2002 Free Software Foundation, Inc.
# This program 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 program 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 program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
AC_DEFUN([AM_AUTOMAKE_VERSION],[am__api_version="1.4"])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION so it can be traced.
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.4-p6])])
# #
# Check to make sure that the build environment is sane. # Check to make sure that the build environment is sane.
# #
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
/* Define if you have a working `mmap' system call. */ /* Define if you have a working `mmap' system call. */
#undef HAVE_MMAP #undef HAVE_MMAP
/* Define if you need to in order for stat and other things to work. */
#undef _POSIX_SOURCE
// Define if GCC supports weak symbols. // Define if GCC supports weak symbols.
#undef _GLIBCPP_SUPPORTS_WEAK #undef _GLIBCPP_SUPPORTS_WEAK
......
This source diff could not be displayed because it is too large. You can view the blob instead.
// 2001-12-27 pme // 2001-12-27 pme
// //
// Copyright (C) 2001 Free Software Foundation, Inc. // Copyright (C) 2001, 2002 Free Software Foundation, Inc.
// //
// This file is part of the GNU ISO C++ Library. This library is free // 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 // software; you can redistribute it and/or modify it under the
...@@ -21,12 +21,16 @@ ...@@ -21,12 +21,16 @@
// 23.2.1.1 deque constructors, copy, and assignment // 23.2.1.1 deque constructors, copy, and assignment
#include <deque> #include <deque>
#include <iterator>
#include <sstream>
#include <testsuite_allocator.h>
#include <testsuite_hooks.h> #include <testsuite_hooks.h>
typedef std::deque<gnu_counting_struct> gdeque; typedef std::deque<gnu_counting_struct> gdeque;
bool test = true;
// basic alloc/dealloc sanity check // see http://gcc.gnu.org/ml/libstdc++/2001-11/msg00139.html
void void
test01() test01()
{ {
...@@ -38,9 +42,490 @@ test01() ...@@ -38,9 +42,490 @@ test01()
assert_count (0); 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() 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(); test01();
return 0; return !test;
} }
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <vector> #include <vector>
#include <stdexcept> #include <stdexcept>
#include <testsuite_allocator.h>
#include <testsuite_hooks.h> #include <testsuite_hooks.h>
template<typename T> template<typename T>
...@@ -119,10 +120,72 @@ void test03() ...@@ -119,10 +120,72 @@ void test03()
VERIFY( test ); 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() int main()
{ {
test01(); test01();
test02(); test02();
test03(); test03();
test_reserve();
test_reserve_exception_guarantee();
return 0; return 0;
} }
// 1999-06-29 bkoz // 1999-06-29 bkoz
// Copyright (C) 1999-2001 Free Software Foundation, Inc. // Copyright (C) 1999-2001, 2002 Free Software Foundation, Inc.
// //
// This file is part of the GNU ISO C++ Library. This library is free // 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 // software; you can redistribute it and/or modify it under the
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <testsuite_allocator.h>
#include <testsuite_hooks.h> #include <testsuite_hooks.h>
template<typename T> template<typename T>
...@@ -94,12 +95,550 @@ void test04() ...@@ -94,12 +95,550 @@ void test04()
#endif #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() int main()
{ {
test01(); test01();
test02(); test02();
test03(); test03();
test04(); 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; return 0;
} }
...@@ -51,7 +51,7 @@ INCLUDES = \ ...@@ -51,7 +51,7 @@ INCLUDES = \
## Build support library. ## Build support library.
noinst_LIBRARIES = libv3test.a noinst_LIBRARIES = libv3test.a
libv3test_a_SOURCES = testsuite_hooks.cc libv3test_a_SOURCES = testsuite_hooks.cc testsuite_allocator.cc
## Build support utilities. ## Build support utilities.
## Only build this as native, as need to find startup files and libc to link. ## Only build this as native, as need to find startup files and libc to link.
......
...@@ -162,7 +162,7 @@ INCLUDES = \ ...@@ -162,7 +162,7 @@ INCLUDES = \
noinst_LIBRARIES = libv3test.a noinst_LIBRARIES = libv3test.a
libv3test_a_SOURCES = testsuite_hooks.cc libv3test_a_SOURCES = testsuite_hooks.cc testsuite_allocator.cc
@GLIBCPP_BUILD_ABI_CHECK_TRUE@noinst_PROGRAMS = @GLIBCPP_BUILD_ABI_CHECK_TRUE@abi_check @GLIBCPP_BUILD_ABI_CHECK_TRUE@noinst_PROGRAMS = @GLIBCPP_BUILD_ABI_CHECK_TRUE@abi_check
@GLIBCPP_BUILD_ABI_CHECK_FALSE@noinst_PROGRAMS = @GLIBCPP_BUILD_ABI_CHECK_FALSE@noinst_PROGRAMS =
abi_check_SOURCES = abi_check.cc abi_check_SOURCES = abi_check.cc
...@@ -177,7 +177,8 @@ CPPFLAGS = @CPPFLAGS@ ...@@ -177,7 +177,8 @@ CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@ LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ LIBS = @LIBS@
libv3test_a_LIBADD = libv3test_a_LIBADD =
libv3test_a_OBJECTS = testsuite_hooks.$(OBJEXT) libv3test_a_OBJECTS = testsuite_hooks.$(OBJEXT) \
testsuite_allocator.$(OBJEXT)
@GLIBCPP_BUILD_ABI_CHECK_FALSE@noinst_PROGRAMS = @GLIBCPP_BUILD_ABI_CHECK_FALSE@noinst_PROGRAMS =
PROGRAMS = $(noinst_PROGRAMS) PROGRAMS = $(noinst_PROGRAMS)
......
//
// 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;
// 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
...@@ -78,6 +78,10 @@ __set_testsuite_memlimit(float) { } ...@@ -78,6 +78,10 @@ __set_testsuite_memlimit(float) { }
gnu_counting_struct::size_type gnu_counting_struct::count = 0; gnu_counting_struct::size_type gnu_counting_struct::count = 0;
int gnu_copy_tracker::itsCopyCount = 0; unsigned int gnu_copy_constructor::count_ = 0;
int gnu_copy_tracker::itsDtorCount = 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;
...@@ -100,58 +100,177 @@ struct gnu_counting_struct ...@@ -100,58 +100,177 @@ struct gnu_counting_struct
#define assert_count(n) VERIFY(gnu_counting_struct::count == n) #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 class gnu_copy_tracker
{ {
public: public:
// Cannot be explicit. Conversion ctor used by list_modifiers.cc's // Creates a copy-tracking object with the given ID number.
// test03(), "range fill at beginning". // If "throw_on_copy" is set, an exception will be thrown if
gnu_copy_tracker (int anId, bool throwOnDemand = false) // an attempt is made to copy this object.
: itsId(anId), willThrow(throwOnDemand) gnu_copy_tracker(int id = next_id_--, bool throw_on_copy = false)
{} : id_(id)
, throw_on_copy_(throw_on_copy)
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) // 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_)
{ {
itsId = rhs.id(); int kkk = throw_on_copy_;
// willThrow must obviously already be false to get this far if (throw_on_copy_)
{
gnu_copy_constructor::throw_on(gnu_copy_constructor::count() + 1);
}
gnu_copy_constructor::mark_call();
} }
~gnu_copy_tracker() { ++itsDtorCount; } // 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 int
id() const id() const
{ return itsId; } { return id_; }
private: private:
int itsId; int id_;
const bool willThrow; const bool throw_on_copy_;
public: public:
static void static void
reset() reset()
{ itsCopyCount = 0; itsDtorCount = 0; } {
gnu_copy_constructor::reset();
gnu_assignment_operator::reset();
gnu_destructor::reset();
}
// for backwards-compatibility
static int static int
copyCount() copyCount()
{ return itsCopyCount; } { return gnu_copy_constructor::count(); }
// for backwards-compatibility
static int static int
dtorCount() dtorCount()
{ return itsDtorCount; } { return gnu_destructor::count(); }
private: private:
static int itsCopyCount; static int next_id_;
static int itsDtorCount;
}; };
inline bool
operator==(const gnu_copy_tracker& lhs, const gnu_copy_tracker& rhs)
{ return lhs.id() == rhs.id(); }
struct gnu_char struct gnu_char
{ {
unsigned long c; unsigned long c;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment