/*************************************************************************** * * 23.vector.cons.cpp - test exercising [lib.vector.cons] * * $Id: 23.vector.cons.cpp 550991 2007-06-26 23:58:07Z sebor $ * *************************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * * Copyright 2006 Rogue Wave Software. * **************************************************************************/ #include // for placement operator new() #include // for vector #include // for size_t #include #include // for UserClass #include #include /**************************************************************************/ static unsigned rw_opt_nloops; static int rw_opt_no_int_spec; static int rw_opt_no_short_spec; static int rw_opt_no_types; static int rw_opt_no_signatures; static int rw_opt_no_ctors; /**************************************************************************/ template struct test_iterator { test_iterator (); }; /**************************************************************************/ // outlined to prevent gcc -Winline warnings template test_iterator:: test_iterator () { static int i; // zeroed out if (++i) // prevent "unreachable code" warnings return; // compile only // exercise RandomAccessIterator requirements RandomAccessIterator it = RandomAccessIterator (); RandomAccessIterator it_cpy (it); Difference d (it - it); RandomAccessIterator &it_ref1 = ++it; const RandomAccessIterator &it_ref2 = it++; RandomAccessIterator &it_ref3 = --it; const RandomAccessIterator &it_ref4 = it--; RandomAccessIterator &it_ref5 = it += 1; it_cpy = it + d; it_cpy = d + it; RandomAccessIterator &it_ref6 = it -= 1; it_cpy = it - d; T t (it [d]); Reference ref1 (*it); Reference ref2 (*it++); bool b (it == it); b = it != it; b = it > it; b = it >= it; b = it <= it; _RWSTD_UNUSED (it); _RWSTD_UNUSED (it_cpy); _RWSTD_UNUSED (it_ref1); _RWSTD_UNUSED (it_ref2); _RWSTD_UNUSED (it_ref3); _RWSTD_UNUSED (it_ref4); _RWSTD_UNUSED (it_ref5); _RWSTD_UNUSED (it_ref6); _RWSTD_UNUSED (d); _RWSTD_UNUSED (t); _RWSTD_UNUSED (ref1); _RWSTD_UNUSED (ref2); _RWSTD_UNUSED (b); } /***************************************************************************/ // verify that nested types are properly defined template void test_types (Vector*, T*, Allocator) { #define TEST_TYPE(T, vectorT) { \ T *t = (typename Vector::vectorT*)0; \ _RWSTD_UNUSED (t); \ } #define TEST_REFERENCE(T, vectorT) { \ typename Vector::value_type v; \ T t = v; \ _RWSTD_UNUSED (t); \ } // verify nested types TEST_TYPE (T, value_type); TEST_TYPE (Allocator, allocator_type); TEST_TYPE (typename Allocator::pointer, pointer); TEST_TYPE (typename Allocator::const_pointer, const_pointer); #ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC typedef std::reverse_iterator ReverseIterator; typedef std::reverse_iterator ConstReverseIterator; #else // if defined (_RWSTD_NO_CLASS_PARTIAL_SPEC) typedef std::reverse_iterator ReverseIterator; typedef std::reverse_iterator ConstReverseIterator; #endif // _RWSTD_NO_CLASS_PARTIAL_SPEC TEST_TYPE (ReverseIterator, reverse_iterator); TEST_TYPE (ConstReverseIterator, const_reverse_iterator); test_iterator ti; // exercise iterator requirements test_iterator tci; // exercise iterator requirements test_iterator tri; // exercise iterator requirements test_iterator tcri; // exercise iterator requirements _RWSTD_UNUSED (ti); _RWSTD_UNUSED (tci); _RWSTD_UNUSED (tri); _RWSTD_UNUSED (tcri); // special treatment TEST_REFERENCE (typename Allocator::reference, reference); TEST_REFERENCE (typename Allocator::const_reference, const_reference); } /**************************************************************************/ // verify that member functions are properly defined template void test_signatures (Vector*, T*, Allocator) { // verify that a member function is accessible and has the appropriate // signature, including return type and exception specification #define MEMFUN(result, name, arg_list) do { \ result (Vector::*pf) arg_list = &Vector::name; \ _RWSTD_UNUSED (pf); \ } while (0) MEMFUN (Vector&, operator=, (const Vector&)); // helpers to work around a SunPro 5.3 bug (see PR #25972) typedef typename Vector::size_type SizeType; typedef typename Vector::value_type ValueType; typedef typename Vector::iterator Iterator; // verify signatures of iterator accessors MEMFUN (typename Vector::iterator, begin, ()); MEMFUN (typename Vector::const_iterator, begin, () const); MEMFUN (typename Vector::iterator, end, ()); MEMFUN (typename Vector::const_iterator, end, () const); // verify signatures of reverse iterator accessors MEMFUN (typename Vector::reverse_iterator, rbegin, ()); MEMFUN (typename Vector::const_reverse_iterator, rbegin, () const); MEMFUN (typename Vector::reverse_iterator, rend, ()); MEMFUN (typename Vector::const_reverse_iterator, rend, () const); // verify signatures of capacity accessors MEMFUN (SizeType, size, () const); MEMFUN (SizeType, max_size, () const); MEMFUN (void, resize, (SizeType, ValueType)); MEMFUN (SizeType, capacity, () const); MEMFUN (bool, empty, () const); MEMFUN (void, reserve, (SizeType)); // verify signature of the element access functions MEMFUN (const T&, operator[], (SizeType) const); MEMFUN (T&, operator[], (SizeType)); MEMFUN (const T&, at, (SizeType) const); MEMFUN (T&, at, (SizeType)); MEMFUN (const T&, front, () const); MEMFUN (T&, front, ()); MEMFUN (const T&, back, () const); MEMFUN (T&, back, ()); // verify signatures of modifiers MEMFUN (void, push_back, (const T&)); MEMFUN (void, pop_back, ()); MEMFUN (Iterator, insert, (Iterator, const T&)); MEMFUN (void, insert, (Iterator, SizeType, const T&)); #if !defined (_MSC_VER) || _MSC_VER >= 1300 // member function template insert MEMFUN (void, insert, (Iterator, InputIter, InputIter)); #endif // !defined (_MSC_VER) || _MSC_VER >= 1300 MEMFUN (Iterator, erase, (Iterator)); MEMFUN (Iterator, erase, (Iterator, Iterator)); MEMFUN (void, swap, (Vector&)); MEMFUN (void, clear, ()); #if !defined (_MSC_VER) || _MSC_VER > 1300 #define FUN(result, name, arg_list) do { \ result (*pf) arg_list = &name; \ _RWSTD_UNUSED (pf); \ } while (0) // verify signatures of non-member functions FUN (bool, std::operator==, (const Vector&, const Vector&)); FUN (bool, std::operator<, (const Vector&, const Vector&)); FUN (bool, std::operator!=, (const Vector&, const Vector&)); FUN (bool, std::operator>, (const Vector&, const Vector&)); FUN (bool, std::operator>=, (const Vector&, const Vector&)); FUN (bool, std::operator<=, (const Vector&, const Vector&)); FUN (void, std::swap, (Vector&, Vector&)); #else // MSVC <= 7.0 // working around a bug in MSVC 7 and prior (see PR #26625) if (0 /* compile only */) { Vector *pv = 0; const Vector *pcv = 0; bool b; b = std::operator== (*pcv, *pcv); b = std::operator< (*pcv, *pcv); b = std::operator!= (*pcv, *pcv); b = std::operator> (*pcv, *pcv); b = std::operator>= (*pcv, *pcv); b = std::operator<= (*pcv, *pcv); std::swap (*pv, *pv); } #endif // MSVC <= 7.0 } /**************************************************************************/ // exercise [lib.vector.cons] and vector<>::operator=() // focus on the correct construction and destruction of values void test_ctors () { typedef std::vector > Vector; if (1) { rw_info (0, 0, 0, "std::vector::vector(size_type, " "const_reference, const allocator_type&)"); rw_info (0, 0, 0, "std::vector::vector(const vector&)"); // reset function call counters UserClass::reset_totals (); // total number of objects of type UserClass in existence const std::size_t x_count = UserClass::count_; for (Vector::size_type i = 0; i != rw_opt_nloops; ++i) { rw_assert (UserClass::count_ == x_count, 0, __LINE__, "vector::vector(size_type, const_reference); " "leaked %zu objects of value_type", UserClass::count_ - x_count); // reset function call counters UserClass::reset_totals (); const UserClass val; // initialize a vector with `i' copies of `val' const Vector v0 (i, val); rw_assert ( i == v0.size () && v0.begin () + i == v0.end () && v0.rbegin () + i == v0.rend (), 0, __LINE__, "vector::vector(size_type, const_reference)"); // verify that the vector ctor calls only copy ctor // of UserClass and anly the given number of times each rw_assert (UserClass::is_total (i + 1, 1, i, 0, 0, 0), 0, __LINE__, "vector::vector(size_type, const_reference); " "called default/copy ctor and operator=() %zu, %zu, " "and %zu times, respectively, 0, %zu, 0 expected", UserClass::n_total_def_ctor_ - 1, UserClass::n_total_copy_ctor_, UserClass::n_total_op_assign_, i); // create a copy Vector v1 (v0); rw_assert ( i == v1.size () && v1.begin () + i == v1.end () && v1.rbegin () + i == v1.rend (), 0, __LINE__, "vector::vector(const vector&)"); // verify that the vector copy ctor calls only copy ctor // of UserClass and anly the given number of times each rw_assert (UserClass::is_total (2 * i + 1, 1, 2 * i, 0, 0, 0), 0, __LINE__, "vector::vector(size_type, const_reference); " "called default/copy ctor and operator=() %zu, %zu, " "and %zu times, respectively, 0, %zu, 0 expected", UserClass::n_total_def_ctor_ - 1, UserClass::n_total_copy_ctor_, UserClass::n_total_op_assign_, i); // exercise vector<>operator=(const vector&) Vector v2 (i, val); for (Vector::size_type j = 0; j != rw_opt_nloops; ++j) { Vector v3 (j, val); // assign a vector (of a possibly unequal size) v3 = v2; rw_assert (v3.size () == v2.size (), 0, __LINE__, "%zu. vector::operator=(const vector&)", j); rw_assert (v3 == v2, 0, __LINE__, "%zu. vector::operator=(const vector&)", j); } #ifndef _RWSTD_NO_EXCEPTIONS // exercise vector exception safety bool thrown = false; // create a suitably aligned buffer in which to construct // the vector object union { void *pad; char buf [sizeof (Vector)]; } buf = { 0 }; std::size_t x_count_save = UserClass::count_; try { // have UserClass copy ctor throw an exception during // the copying of the last value UserClass::copy_ctor_throw_count_ = UserClass::n_total_copy_ctor_ + i; // create a vector object, throw an exception // expect vector ctor to destroy any values // constructed prior to throwing the exception // and to free all already allocated memory // use placement new to prevent vector destruction // at scope exit (the object should be destroyed // during stack unwinding instead) Vector *p = new (&buf.buf) Vector (i, val); // destroy explicitly if ctor doesn't propagate exception #if !defined (__HP_aCC) || _RWSTD_HP_aCC_MINOR > 3800 p->~Vector (); #else // work around aCC bug (see PR #25356) p->~vector (); #endif // HP aCC } catch (...) { thrown = true; } // no exception thrown if (i == 0), no elements constructed rw_assert (i == 0 || thrown, 0, __LINE__, "logic error: failed to throw"); rw_assert (x_count_save == UserClass::count_, 0, __LINE__, "vector::vector(size_type, const_reference) " "leaked %zu value(s) of %zu after an exception", UserClass::count_ - x_count_save, i); // exercise vector<>::vector(const vector&) thrown = false; x_count_save = UserClass::count_; try { // have UserClass copy ctor throw an exception during // the copying of the last value UserClass::copy_ctor_throw_count_ = UserClass::n_total_copy_ctor_ + i; // use placement new to prevent vector destruction // at scope exit (the object should be destroyed // during stack unwinding instead) Vector *p = new (&buf) Vector (v1); // destroy explicitly if ctor doesn't propagate exception #if !defined (__HP_aCC) || _RWSTD_HP_aCC_MINOR > 3800 p->~Vector (); #else // work around aCC bug (see PR #25356) p->~vector (); #endif // HP aCC } catch (...) { // vector should have been destroyed thrown = true; } // no exception thrown if (i == 0), no elements constructed rw_assert(i == 0 || thrown, 0, __LINE__, "logic error: failed to throw"); rw_assert (x_count_save == UserClass::count_, 0, __LINE__, "vector::vector(const vector&) leaked " "%zu value(s) of %zu after an exception", UserClass::count_ - x_count_save, i); // disable exceptions UserClass::copy_ctor_throw_count_ = std::size_t (-1); // remember v1's size and capacity const Vector::size_type v1_size = v1.size (); const Vector::size_type v1_cap = v1.capacity (); // create and initialize vector with some values // make sure v3's capacity is greater than that of v1 // (otherwise the assignment isn't exception safe since // it simply overwrites existing values for efficiency) const Vector v3 (v1_cap * 2, val); // exrecise vector<>::operator=(const vector&) thrown = false; x_count_save = UserClass::count_; try { // have UserClass copy ctor throw an exception during // the copying of the last value UserClass::copy_ctor_throw_count_ = UserClass::n_total_copy_ctor_ + v3.size (); // assign over the existing elements, the last copy ctor or // operator=() throws, destroying all values assigned so far v1 = v3; } catch (...) { // original vector must remain unchanged thrown = true; } // disable exceptions UserClass::copy_ctor_throw_count_ = std::size_t (-1); rw_assert (i == 0 || thrown, 0, __LINE__, "logic error: failed to throw"); // verify that no values leaked rw_assert (x_count_save == UserClass::count_, 0, __LINE__, "vector::vector(const vector&) leaked " "%zu value(s) of %zu after an exception", UserClass::count_ - x_count_save, i); // verify that the size of the left hand size operand // of the assignment hasn't changed rw_assert (v1.size () == v1_size && v1.capacity () == v1_cap, 0, __LINE__, "vector::operator=" "(const vector&) changed size of *this from " "%zu to %zu after exception", i / 2, v3.size ()); #endif // _RWSTD_NO_EXCEPTIONS // vectors go out of scope, must destroy all elements } } } // exercise [lib.vector.cons] template void test_ctors (Vector*, T*, Alloc alloc) { if (1) { rw_info (0, 0, 0, "std::vector::vector(const allocator_type&)"); // verify default ctor arguments Vector v0; Vector v1 (alloc); rw_assert ( 0 == v0.size () && v0.empty () && v0.begin () == v0.end () && v0.rbegin () == v0.rend (), 0, __LINE__, ("vector::vector()")); rw_assert ( 0 == v1.size () && v1.empty () && v1.begin () == v1.end () && v1.rbegin () == v1.rend (), 0, __LINE__, "vector::vector()"); } if (1) { rw_info (0, 0, 0, "std::vector::vector(size_type, " "const_reference, const allocator_type&)"); for (typename Vector::size_type i = 0; i != rw_opt_nloops; ++i) { const T val = i; Vector v0 (i, val); Vector v1 (i, val, alloc); rw_assert ( i == v0.size () && v0.begin () + i == v0.end () && v0.rbegin () + i == v0.rend (), 0, __LINE__, "vector::vector" "(size_type, const_reference)"); rw_assert ( i == v1.size () && v1.begin () + i == v1.end () && v1.rbegin () + i == v1.rend (), 0, __LINE__, "vector::vector(size_type, " "const_reference, const allocator_type&)"); bool success = true; for (typename Vector::size_type j = 0; j != i; ++j) { if (!(success = v0 [j] == val)) break; if (!(success = v1 [j] == val)) break; } rw_assert (success, 0, __LINE__, "vector::vector(size_type, const_reference); " "all elements initialized"); } } #if !defined (_MSC_VER) || _MSC_VER >= 1300 if (1) { rw_info (0, 0, 0, "template std::vector::vector" "(InputIterator, InputIterator)"); bool success = true; // allocate nloops elements, do not initialize typename Vector::value_type *vals = alloc.allocate (rw_opt_nloops); for (typename Vector::size_type i = 0; i != rw_opt_nloops; ++i) { // construct an element at then end of array alloc.construct (vals + i, i); // verify ctor with a strict InputIterator InputIter first (vals, vals, vals + i); InputIter last (vals + i, vals + i, vals + i); const Vector v0 (first, last); // reset iterators since they are single-pass and their // copies have been passed through by the ctor above first = InputIter(vals, vals, vals + i); last = InputIter(vals + i, vals + i, vals + i); const Vector v1 (first, last, alloc); if ( i != v0.size () || (!(i && !v0.empty () || !i && v0.empty ())) || (i != v1.size ()) || (!(i && !v1.empty () || !i && v1.empty ()))) success = false; // verify size() and empty() rw_assert (i == v0.size (), 0, __LINE__, "size () == %zu, got %zu", i, v0.size ()); rw_assert (i && !v0.empty () || !i && v0.empty (), 0, __LINE__, "size () == %zu, empty () == %d", v0.size (), v0.empty ()); rw_assert (i == v1.size (), 0, __LINE__, "size () == %zu, got %zu", i, v1.size ()); rw_assert (i && !v1.empty () || !i && v1.empty (), 0, __LINE__, "size () == %zu, empty () == %d", v1.size (), v1.empty ()); } rw_assert (success, 0, __LINE__, "template " "std::vector::vector" "(InputIterator, InputIterator)"); // destroy and deallocate... for (typename Vector::size_type j = 0; j != rw_opt_nloops; ++j) alloc.destroy (vals + j); alloc.deallocate (vals, rw_opt_nloops); } #endif // !defined (_MSC_VER) || _MSC_VER >= 1300 } /**************************************************************************/ static int run_test (int /* argc */, char** /* argv */) { static int int_noted = 0; static int short_noted = 0; static int types_noted = 0; static int sign_noted = 0; static int ctors_noted = 0; #ifndef _RWSTD_NO_EXCEPTIONS try { // throw an exception to initialize the lib (allocates // memory that's never deallocated; shows up as leaks) _RW::__rw_throw (_RWSTD_ERROR_LOGIC_ERROR, "", ""); } catch (...) { } #endif // _RWSTD_NO_EXCEPTIONS // for convenience (in case default template arguments are disabled) #define VECTOR(T) std::vector > #define TEST(what, T) \ do { \ /* establish a checkpoint for memory leaks */ \ rwt_check_leaks (0, 0); \ test_ ##what ((VECTOR (T)*)0, (T*)0, std::allocator()); \ /* verify that no memory leaked */ \ std::size_t nbytes; \ const std::size_t nblocks = rwt_check_leaks (&nbytes, 0); \ rw_assert (!nblocks && !nbytes, 0, __LINE__, \ #what " test leaked %lu bytes in %lu blocks", \ nbytes, nblocks); \ } while (0) if (rw_opt_no_int_spec) { rw_note (int_noted++, 0, __LINE__, "int specializations test disabled."); } else { // exercise vector and its default template argument if (rw_opt_no_types) { rw_note (types_noted++, 0, __LINE__, "Types test disabled."); } else { TEST (types, int); } if (rw_opt_no_signatures) { rw_note (sign_noted++, 0, __LINE__, "Signatures test disabled."); } else { TEST (signatures, int); } if (rw_opt_no_ctors) { rw_note (ctors_noted++, 0, __LINE__, "Ctors test disabled."); } else { TEST (ctors, int); } } // exercise vector with a template argument other than the default #undef VECTOR #define VECTOR(T, A) std::vector > #undef TEST #define TEST(what,T,A) test_ ##what ((VECTOR (T, A)*)0, (T*)0, A()) if (rw_opt_no_short_spec) { rw_note (short_noted++, 0, __LINE__, "short specializations test disabled."); } else { if (rw_opt_no_types) { rw_note (types_noted++, 0, __LINE__, "Types test disabled."); } else { TEST (types, short, std::allocator); } if (rw_opt_no_signatures) { rw_note (sign_noted++, 0, __LINE__, "Signatures test disabled."); } else { TEST (signatures, short, std::allocator); } if (rw_opt_no_ctors) { rw_note (ctors_noted++, 0, __LINE__, "Ctors test disabled."); } else { TEST (ctors, short, std::allocator); } } if (rw_opt_no_ctors) { rw_note (ctors_noted++, 0, __LINE__, "Ctors test disabled."); } else { test_ctors (); } return 0; } /**************************************************************************/ int main (int argc, char** argv) { return rw_test (argc, argv, __FILE__, "lib.vector.cons", 0 /* no comment */, run_test, "|-nloops#1 " "|-no-int_specializations# " "|-no-short_specializations# " "|-no-types# " "|-no-signatures# " "|-no-ctors#", &rw_opt_nloops, &rw_opt_no_int_spec, &rw_opt_no_short_spec, &rw_opt_no_types, &rw_opt_no_signatures, &rw_opt_no_ctors); }