diff --git a/auxi/impl_algo.h b/auxi/impl_algo.h index 3fd29401..90efbad7 100644 --- a/auxi/impl_algo.h +++ b/auxi/impl_algo.h @@ -46,7 +46,7 @@ namespace oel::_detail template< typename T > - T * Relocate(T *__restrict src, size_t const n, T *__restrict dest) + T * Relocate(T *__restrict src, size_t const n, T *__restrict dest) noexcept { if constexpr (is_trivially_relocatable::value) { diff --git a/dynarray.h b/dynarray.h index 16c05a0e..33d9e538 100644 --- a/dynarray.h +++ b/dynarray.h @@ -1,6 +1,6 @@ #pragma once -// Copyright 2014, 2015 Ole Erik Peistorpet +// Copyright 2015 Ole Erik Peistorpet // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -159,7 +159,7 @@ class dynarray void resize_for_overwrite(size_type n) { _resizeImpl<_detail::UninitDefaultConstruct>(n); } void resize(size_type n) { _resizeImpl<_uninitFill>(n); } - //! @brief Equivalent to `std::vector::insert(pos, begin(source), end(source))`, + //! @brief Same as `std::vector::insert(pos, begin(source), end(source))`, //! where `end(source)` is not needed if `source.size()` exists template< typename ForwardRange > iterator insert_range(const_iterator pos, ForwardRange && source) &; @@ -303,39 +303,15 @@ class dynarray } _m; // the only non-static data member - struct _scopedPtr : private _alloc_7KQWe - { - pointer data; // owner - pointer bufEnd; - - _scopedPtr(const _alloc_7KQWe & a, pointer buf, size_type capacity) - : _alloc_7KQWe{a}, data{buf}, bufEnd{buf + capacity} { - } - - _scopedPtr(_scopedPtr &&) = delete; - - ~_scopedPtr() - { - if (data) - ::oel::_detail::DebugAllocateWrapper<_alloc_7KQWe, pointer>::dealloc(*this, data, bufEnd - data); - } - }; - using _uninitFill = _detail::UninitFill<_memOwner>; - void _resetData(T *const newData) + void _resetData(T *const newData, size_type const newCap) { if (_m.data) _allocateWrap::dealloc(_m, _m.data, capacity()); - - _m.data = newData; - } - - void _swapBuf(_scopedPtr & s) noexcept - { // Missing _debugSizeUpdater here, but only used in _insertRealloc, which is guarded - using std::swap; - swap(_m.data, s.data); - swap(_m.reservEnd, s.bufEnd); + // Beware, sets _m.data with no _debugSizeUpdater + _m.data = newData; + _m.reservEnd = newData + newCap; } void _initReserve(size_type const capToCheck) @@ -396,7 +372,7 @@ class dynarray { constexpr auto startBytesGood = std::max(3 * sizeof(void *), 4 * sizeof(int)); constexpr auto minGrow = (startBytesGood + sizeof(T) - 1) / sizeof(T); - size_type const c = capacity(); + auto const c = capacity(); return c + std::max(c, minGrow); // growth factor is 2 } @@ -421,8 +397,7 @@ class dynarray else { pointer const newData = _allocateWrap::allocate(_m, newCap); _m.end = _detail::Relocate(_m.data, oldSize, newData); - _resetData(newData); - _m.reservEnd = newData + newCap; + _resetData(newData, newCap); } (void) _debugSizeUpdater{_m}; } @@ -432,7 +407,7 @@ class dynarray #endif void _growBy(size_type const count) { - size_type const s = size(); + auto const s = size(); _realloc(_calcCapAdd(count, s), s); } @@ -469,8 +444,8 @@ class dynarray if (capacity() < count) { // Deallocating first might be better, // but then the _m pointers would have to be nulled in case allocate throws - _resetData(_allocateChecked(count)); - _m.end = _m.reservEnd = _m.data + count; + _resetData(_allocateChecked(count), count); + _m.end = _m.reservEnd; } else { _m.end = _m.data + count; @@ -495,9 +470,8 @@ class dynarray T *const newData = _allocateChecked(count); // Old elements might hold some limited resource, probably good to destroy them before constructing new _detail::Destroy(_m.data, _m.end); - _resetData(newData); + _resetData(newData, count); _m.end = newData; - _m.reservEnd = newData + count; newEnd = _m.reservEnd; } else @@ -533,7 +507,7 @@ class dynarray template< typename InputIter, typename Sentinel > InputIter _append(InputIter first, Sentinel const last) { // single-pass iterator and unknown count - size_type const oldSize = size(); + auto const oldSize = size(); OEL_TRY_ { for (; first != last; ++first) @@ -572,57 +546,42 @@ class dynarray } - template< typename InsertHelper, typename... Args > - T * _insertRealloc(T *const pos, Args... args) + T * _insertReallocImpl(size_type const newCap, T *const pos, size_type const count) { - auto const newCap = InsertHelper::calcCap(*this, args...); - _scopedPtr newBuf{_m, _allocateWrap::allocate(_m, newCap), newCap}; - - size_type const nBefore = pos - data(); - T *const newPos = newBuf.data + nBefore; - T *const afterAdded = - InsertHelper::template construct(_m, newPos, static_cast(args)...); + auto const newData = _allocateWrap::allocate(_m, newCap); // Exception free from here - size_type const nAfterPos = _m.end - pos; - _detail::Relocate(_m.data, nBefore, newBuf.data); // prefix - _m.end = _detail::Relocate(pos, nAfterPos, afterAdded); // suffix - _swapBuf(newBuf); + auto const nBefore = pos - _m.data; + auto const nAfter = _m.end - pos; + T *const newPos = _detail::Relocate(_m.data, nBefore, newData); + _m.end = _detail::Relocate(pos, nAfter, newPos + count); + _resetData(newData, newCap); return newPos; } - struct _emplaceHelper + T * _insRangeRealloc(T *const pos, size_type const count) { - template< typename... Unused > - static size_type calcCap(dynarray & self, const Unused &...) - { - return self._calcCapAddOne(); - } - - template< typename... Args > - static T * construct(_memOwner & alloc, T *const newPos, Args... args) - { - _alloTrait::construct(alloc, newPos, static_cast(args)...); - return newPos + 1; - } - }; + auto newCap = _calcCapAdd(count, size()); + return _insertReallocImpl(newCap, pos, count); + } - struct _insertRHelper + T * _emplaceRealloc(T * pos, T & destroyOnFail) { - template< typename InputIter > - static size_type calcCap(dynarray & self, const InputIter &, size_type count) + struct Guard { - return self._calcCapAdd(count, self.size()); - } + T * destroy; - template< typename InputIter, typename > - static T * construct(_memOwner & alloc, T *const newPos, InputIter first, size_type count) - { - T *const dLast = newPos + count; - _detail::UninitCopy(std::move(first), newPos, dLast, alloc); - return dLast; - } - }; + ~Guard() + { + if (destroy) + destroy-> ~T(); + } + } exit{&destroyOnFail}; + + pos = _insertReallocImpl(_calcCapAddOne(), pos, 1); + exit.destroy = nullptr; + return pos; + } }; template< typename T, typename Alloc > @@ -640,24 +599,26 @@ typename dynarray::iterator OEL_ASSERT(_m.data <= pPos and pPos <= _m.end); OEL_DYNARR_INSERT_STEP1 + + // Temporary in case constructor throws or args refer to an element of this dynarray + storage_for tmp; + _alloTrait::construct(_m, reinterpret_cast(&tmp), static_cast(args)...); if (_m.end < _m.reservEnd) - { - // Temporary in case constructor throws or source is an element of this dynarray at pos or after - storage_for tmp; - _alloTrait::construct(_m, reinterpret_cast(&tmp), static_cast(args)...); - // Relocate [pos, end) to [pos + 1, end + 1), leaving memory at pos uninitialized (conceptually) - size_type const bytesAfterPos = sizeof(T) * (_m.end - pPos); + { // Relocate [pos, end) to [pos + 1, end + 1) + size_t const bytesAfterPos{sizeof(T) * (_m.end - pPos)}; std::memmove( static_cast(pPos + 1), static_cast(pPos), bytesAfterPos ); ++_m.end; - std::memcpy(static_cast(pPos), &tmp, sizeof(T)); // relocate the new element to pos } else - { pPos = _insertRealloc< _emplaceHelper, _detail::ForwardT... > - (pPos, static_cast(args)...); + { pPos = _emplaceRealloc(pPos, reinterpret_cast(tmp)); } + std::memcpy( // relocate the new element to pos + static_cast(pPos), + static_cast(&tmp), + sizeof(T) ); return _detail::MakeDynarrIter(_m, pPos); } @@ -666,53 +627,49 @@ template< typename ForwardRange > typename dynarray::iterator dynarray::insert_range(const_iterator pos, ForwardRange && src) & { + OEL_DYNARR_INSERT_STEP1 +#undef OEL_DYNARR_INSERT_STEP1 + auto first = adl_begin(src); auto const count = _detail::CountOrEnd(src); static_assert( std::is_same_v, "insert_range requires that source models std::ranges::forward_range or that source.size() is valid" ); - OEL_DYNARR_INSERT_STEP1 -#undef OEL_DYNARR_INSERT_STEP1 - + size_t const bytesAfterPos{sizeof(T) * (_m.end - pPos)}; + T * dLast; if (_unusedCapacity() >= count) { - T *const dLast = pPos + count; + dLast = pPos + count; // Relocate elements to make space, leaving [pos, pos + count) uninitialized (conceptually) - size_type const bytesAfterPos = sizeof(T) * (_m.end - pPos); - std::memmove( - static_cast(dLast), - static_cast(pPos), - bytesAfterPos ); + std::memmove(static_cast(dLast), static_cast(pPos), bytesAfterPos); _m.end += count; - // Construct new - if constexpr (can_memmove_with) + } + else + { pPos = _insRangeRealloc(pPos, count); + dLast = pPos + count; + } + // Construct new + if constexpr (can_memmove_with) + { + _detail::MemcpyCheck(first, count, pPos); + } + else + { T * dest = pPos; + OEL_TRY_ { - _detail::MemcpyCheck(first, count, pPos); - } - else - { T * dest = pPos; - OEL_TRY_ + while (dest != dLast) { - while (dest != dLast) - { - _alloTrait::construct(_m, dest, *first); - ++first; ++dest; - } - } - OEL_CATCH_ALL - { // relocate back to fill hole - std::memmove( - static_cast(dest), - static_cast(dLast), - bytesAfterPos ); - _m.end -= (dLast - dest); - OEL_RETHROW; + _alloTrait::construct(_m, dest, *first); + ++dest; ++first; } } - } - else - { pPos = _insertRealloc<_insertRHelper>(pPos, std::move(first), count); + OEL_CATCH_ALL + { // relocate back to fill hole + std::memmove(static_cast(dest), static_cast(dLast), bytesAfterPos); + _m.end -= (dLast - dest); + OEL_RETHROW; + } } return _detail::MakeDynarrIter(_m, pPos); } @@ -818,16 +775,14 @@ void dynarray::swap(dynarray & other) noexcept template< typename T, typename Alloc > void dynarray::shrink_to_fit() { - size_type const used = size(); + auto const used = size(); if (0 < used) { _realloc(used, used); } else - { _resetData(nullptr); - _m.reservEnd = _m.end = nullptr; - - (void) _debugSizeUpdater{_m}; + { _m.end = nullptr; + _resetData(nullptr, 0); } } @@ -927,12 +882,12 @@ typename dynarray::iterator dynarray::erase(iterator first, if constexpr (is_trivially_relocatable::value) { _detail::Destroy(dest, pLast); - size_type const nAfterLast = _m.end - pLast; - std::memmove( // relocate [last, end) to [first, first + nAfterLast) + auto const nAfter = _m.end - pLast; + std::memmove( // relocate [last, end) to [first, first + nAfter) static_cast(dest), static_cast(pLast), - sizeof(T) * nAfterLast ); - _m.end = dest + nAfterLast; + sizeof(T) * nAfter ); + _m.end = dest + nAfter; } else if (dest < pLast) // must avoid self-move-assigning the elements { diff --git a/unit_test/dynarray_construct_assignop_swap_gtest.cpp b/unit_test/dynarray_construct_assignop_swap_gtest.cpp index 4547b6e5..568d0629 100644 --- a/unit_test/dynarray_construct_assignop_swap_gtest.cpp +++ b/unit_test/dynarray_construct_assignop_swap_gtest.cpp @@ -14,24 +14,19 @@ int MyCounter::nConstructions; int MyCounter::nDestruct; int MyCounter::countToThrowOn = -1; -int AllocCounter::nAllocations; -int AllocCounter::nDeallocations; -int AllocCounter::nConstructCalls; -std::unordered_map AllocCounter::sizeFromPtr; +TrackingAllocData g_allocCount; using namespace oel; class dynarrayConstructTest : public ::testing::Test { protected: - std::array sizes; + std::array sizes{0, 1, 200}; dynarrayConstructTest() { - AllocCounter::clearAll(); + g_allocCount.clear(); MyCounter::clearCount(); - - sizes = {{0, 1, 200}}; } template @@ -43,7 +38,7 @@ class dynarrayConstructTest : public ::testing::Test for (const auto & e : a) EXPECT_EQ(val, e); - ASSERT_EQ(AllocCounter::nDeallocations + 1, AllocCounter::nAllocations); + ASSERT_EQ(g_allocCount.nDeallocations + 1, g_allocCount.nAllocations); } }; @@ -81,8 +76,8 @@ TEST_F(dynarrayConstructTest, constructEmpty) ASSERT_TRUE(a.get_allocator() == allocator()); EXPECT_EQ(0U, a.capacity()); - EXPECT_EQ(0, AllocCounter::nAllocations); - ASSERT_EQ(0, AllocCounter::nDeallocations); + EXPECT_EQ(0, g_allocCount.nAllocations); + ASSERT_EQ(0, g_allocCount.nDeallocations); } #if defined _CPPUNWIND or defined __EXCEPTIONS @@ -110,7 +105,7 @@ TEST_F(dynarrayConstructTest, constructReserve) { for (auto const n : sizes) { - auto const nExpectAlloc = AllocCounter::nAllocations + 1; + auto const nExpectAlloc = g_allocCount.nAllocations + 1; dynarrayTrackingAlloc a(reserve, n); @@ -118,80 +113,80 @@ TEST_F(dynarrayConstructTest, constructReserve) ASSERT_TRUE(a.capacity() >= n); if (n > 0) - { ASSERT_EQ(nExpectAlloc, AllocCounter::nAllocations); } + { ASSERT_EQ(nExpectAlloc, g_allocCount.nAllocations); } } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); - ASSERT_EQ(0, AllocCounter::nConstructCalls); + ASSERT_EQ(0, g_allocCount.nConstructCalls); } TEST_F(dynarrayConstructTest, constructNDefaultTrivial) { for (auto const n : sizes) { - auto const nExpectAlloc = AllocCounter::nAllocations + 1; + auto const nExpectAlloc = g_allocCount.nAllocations + 1; dynarrayTrackingAlloc a(n, for_overwrite); ASSERT_EQ(a.size(), n); if (n > 0) - { ASSERT_EQ(nExpectAlloc, AllocCounter::nAllocations); } + { ASSERT_EQ(nExpectAlloc, g_allocCount.nAllocations); } } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); - EXPECT_EQ(0, AllocCounter::nConstructCalls); + EXPECT_EQ(0, g_allocCount.nConstructCalls); } TEST_F(dynarrayConstructTest, constructNDefault) { for (auto const n : sizes) { - AllocCounter::nConstructCalls = 0; + g_allocCount.nConstructCalls = 0; NontrivialConstruct::clearCount(); - auto const nExpectAlloc = AllocCounter::nAllocations + 1; + auto const nExpectAlloc = g_allocCount.nAllocations + 1; { dynarrayTrackingAlloc a(n, for_overwrite); - ASSERT_EQ(as_signed(n), AllocCounter::nConstructCalls); + ASSERT_EQ(as_signed(n), g_allocCount.nConstructCalls); ASSERT_EQ(as_signed(n), NontrivialConstruct::nConstructions); ASSERT_EQ(a.size(), n); if (n > 0) - { ASSERT_EQ(nExpectAlloc, AllocCounter::nAllocations); } + { ASSERT_EQ(nExpectAlloc, g_allocCount.nAllocations); } } ASSERT_EQ(NontrivialConstruct::nConstructions, NontrivialConstruct::nDestruct); } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } TEST_F(dynarrayConstructTest, constructN) { for (auto const n : sizes) { - AllocCounter::nConstructCalls = 0; + g_allocCount.nConstructCalls = 0; - auto const nExpectAlloc = AllocCounter::nAllocations + 1; + auto const nExpectAlloc = g_allocCount.nAllocations + 1; dynarrayTrackingAlloc a(n); - ASSERT_EQ(as_signed(n), AllocCounter::nConstructCalls); + ASSERT_EQ(as_signed(n), g_allocCount.nConstructCalls); ASSERT_EQ(a.size(), n); if (n > 0) - { ASSERT_EQ(nExpectAlloc, AllocCounter::nAllocations); } + { ASSERT_EQ(nExpectAlloc, g_allocCount.nAllocations); } } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } TEST_F(dynarrayConstructTest, constructNChar) { for (auto const n : sizes) { - auto const nExpectAlloc = AllocCounter::nAllocations + 1; + auto const nExpectAlloc = g_allocCount.nAllocations + 1; dynarrayTrackingAlloc a(n); @@ -201,9 +196,9 @@ TEST_F(dynarrayConstructTest, constructNChar) ASSERT_EQ(0, c); if (n > 0) - { ASSERT_EQ(nExpectAlloc, AllocCounter::nAllocations); } + { ASSERT_EQ(nExpectAlloc, g_allocCount.nAllocations); } } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } TEST_F(dynarrayConstructTest, constructNFillTrivial) @@ -227,11 +222,11 @@ TEST_F(dynarrayConstructTest, constructNFill) EXPECT_EQ(1 + 11, TrivialRelocat::nConstructions); - ASSERT_EQ(AllocCounter::nDeallocations + 1, AllocCounter::nAllocations); + ASSERT_EQ(g_allocCount.nDeallocations + 1, g_allocCount.nAllocations); } ASSERT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } @@ -244,7 +239,7 @@ TEST_F(dynarrayConstructTest, constructInitList) ASSERT_TRUE(std::equal( a.begin(), a.end(), begin(il) )); ASSERT_EQ(4U, a.size()); - ASSERT_EQ(1, AllocCounter::nAllocations); + ASSERT_EQ(1, g_allocCount.nAllocations); } { dynarrayTrackingAlloc a(std::initializer_list{}); @@ -252,7 +247,7 @@ TEST_F(dynarrayConstructTest, constructInitList) ASSERT_TRUE(a.empty()); EXPECT_EQ(0, TrivialRelocat::nConstructions); } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } TEST_F(dynarrayConstructTest, deductionGuides) @@ -339,7 +334,7 @@ void testMoveConstruct(Alloc a0, Alloc a1) for (int i = 0; i < nr; ++i) right.emplace_back(0.5); - auto const nAllocBefore = AllocCounter::nAllocations; + auto const nAllocBefore = g_allocCount.nAllocations; auto const ptr = right.data(); @@ -347,7 +342,7 @@ void testMoveConstruct(Alloc a0, Alloc a1) EXPECT_TRUE(left.get_allocator() == a1); - EXPECT_EQ(nAllocBefore, AllocCounter::nAllocations); + EXPECT_EQ(nAllocBefore, g_allocCount.nAllocations); EXPECT_EQ(nr, MoveOnly::nConstructions - MoveOnly::nDestruct); EXPECT_TRUE(right.empty()); @@ -355,7 +350,7 @@ void testMoveConstruct(Alloc a0, Alloc a1) ASSERT_EQ(nr, ssize(left)); EXPECT_EQ(ptr, left.data()); } - EXPECT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } TEST_F(dynarrayConstructTest, moveConstructWithAlloc) @@ -384,7 +379,7 @@ TEST_F(dynarrayConstructTest, moveConstructNonAssignable) template void testConstructMoveElements() { - AllocCounter::clearAll(); + g_allocCount.clear(); T::clearCount(); // not propagating, not equal, cannot steal the memory for (auto const na : {0, 1, 101}) @@ -397,7 +392,7 @@ void testConstructMoveElements() auto const capBefore = a.capacity(); - auto const nExpectAlloc = AllocCounter::nAllocations + (a.empty() ? 0 : 1); + auto const nExpectAlloc = g_allocCount.nAllocations + (a.empty() ? 0 : 1); dynarray b(std::move(a), Alloc{2}); @@ -406,7 +401,7 @@ void testConstructMoveElements() EXPECT_EQ(1, a.get_allocator().id); EXPECT_EQ(2, b.get_allocator().id); - EXPECT_EQ(nExpectAlloc, AllocCounter::nAllocations); + EXPECT_EQ(nExpectAlloc, g_allocCount.nAllocations); EXPECT_EQ(ssize(a) + ssize(b), T::nConstructions - T::nDestruct); @@ -416,7 +411,7 @@ void testConstructMoveElements() EXPECT_TRUE(b[i].hasValue() and *b[i] == i + 0.5); } EXPECT_EQ(T::nConstructions, T::nDestruct); - EXPECT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } TEST_F(dynarrayConstructTest, moveConstructUnequalAlloc) @@ -439,7 +434,7 @@ void testMoveAssign(Alloc a0, Alloc a1) for (int i = 0; i < nl; ++i) left.emplace_back(0.5); - auto const nAllocBefore = AllocCounter::nAllocations; + auto const nAllocBefore = g_allocCount.nAllocations; auto const ptr = right.data(); @@ -450,7 +445,7 @@ void testMoveAssign(Alloc a0, Alloc a1) else EXPECT_TRUE(left.get_allocator() == a1); - EXPECT_EQ(nAllocBefore, AllocCounter::nAllocations); + EXPECT_EQ(nAllocBefore, g_allocCount.nAllocations); EXPECT_EQ(9, MoveOnly::nConstructions - MoveOnly::nDestruct); if (0 == nl) @@ -461,7 +456,7 @@ void testMoveAssign(Alloc a0, Alloc a1) ASSERT_EQ(9U, left.size()); EXPECT_EQ(ptr, left.data()); } - EXPECT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } TEST_F(dynarrayConstructTest, moveAssign) @@ -499,14 +494,14 @@ void testAssignMoveElements() auto const capBefore = a.capacity(); - auto const nExpectAlloc = AllocCounter::nAllocations + (b.size() < a.size() ? 1 : 0); + auto const nExpectAlloc = g_allocCount.nAllocations + (b.size() < a.size() ? 1 : 0); b = std::move(a); EXPECT_EQ(1, a.get_allocator().id); EXPECT_EQ(2, b.get_allocator().id); - EXPECT_EQ(nExpectAlloc, AllocCounter::nAllocations); + EXPECT_EQ(nExpectAlloc, g_allocCount.nAllocations); EXPECT_EQ(ssize(a) + ssize(b), T::nConstructions - T::nDestruct); @@ -516,7 +511,7 @@ void testAssignMoveElements() EXPECT_TRUE(b[i].hasValue() and *b[i] == i + 0.5); } EXPECT_EQ(T::nConstructions, T::nDestruct); - EXPECT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } } @@ -597,7 +592,7 @@ TEST_F(dynarrayConstructTest, selfCopyAssign) EXPECT_EQ(5, *nt[0]); } ASSERT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); - EXPECT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } #if OEL_HAS_EXCEPTIONS @@ -619,7 +614,7 @@ void testConstructNThrowing(const Arg &... arg) { for (auto i : {0, 1, 99}) { - AllocCounter::nConstructCalls = 0; + g_allocCount.nConstructCalls = 0; T::clearCount(); T::countToThrowOn = i; @@ -627,11 +622,11 @@ void testConstructNThrowing(const Arg &... arg) dynarrayTrackingAlloc a(100, arg...), TestException ); - ASSERT_EQ(i + 1, AllocCounter::nConstructCalls); + ASSERT_EQ(i + 1, g_allocCount.nConstructCalls); ASSERT_EQ(i, T::nConstructions); ASSERT_EQ(i, T::nDestruct); - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } } @@ -654,25 +649,25 @@ TEST_F(dynarrayConstructTest, copyConstructThrowing) { dynarrayTrackingAlloc a(100, TrivialRelocat{0.5}); - AllocCounter::nAllocations = 0; + g_allocCount.nAllocations = 0; for (auto i : {0, 1, 99}) { - AllocCounter::nConstructCalls = 0; + g_allocCount.nConstructCalls = 0; TrivialRelocat::clearCount(); TrivialRelocat::countToThrowOn = i; - auto const nExpectAlloc = AllocCounter::nAllocations + 1; + auto const nExpectAlloc = g_allocCount.nAllocations + 1; ASSERT_THROW( auto b(a), TestException ); - ASSERT_EQ(i + 1, AllocCounter::nConstructCalls); + ASSERT_EQ(i + 1, g_allocCount.nConstructCalls); ASSERT_EQ(i, TrivialRelocat::nConstructions); ASSERT_EQ(i, TrivialRelocat::nDestruct); - ASSERT_EQ(nExpectAlloc, AllocCounter::nAllocations); - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + ASSERT_EQ(nExpectAlloc, g_allocCount.nAllocations); + ASSERT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } } #endif diff --git a/unit_test/dynarray_mutate_gtest.cpp b/unit_test/dynarray_mutate_gtest.cpp index 952bb8f3..1675c4af 100644 --- a/unit_test/dynarray_mutate_gtest.cpp +++ b/unit_test/dynarray_mutate_gtest.cpp @@ -38,7 +38,10 @@ class dynarrayTest : public ::testing::Test protected: ~dynarrayTest() { - AllocCounter::clearAll(); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); + EXPECT_EQ(MyCounter::nConstructions, MyCounter::nDestruct); + + g_allocCount.clear(); MyCounter::clearCount(); } }; @@ -141,7 +144,7 @@ void testPushBack2() #if OEL_HAS_EXCEPTIONS T::countToThrowOn = 0; - EXPECT_THROW( da.push_back(T{0}), TestException ); + EXPECT_THROW( da.emplace_back(0), TestException ); ASSERT_EQ(expected.size(), da.size()); #endif EXPECT_EQ(T::nConstructions - ssize(da), T::nDestruct); @@ -188,7 +191,7 @@ struct ConstructFromRef } }; -TEST_F(dynarrayTest, emplaceReferencePreserve) +TEST_F(dynarrayTest, emplaceRefTypePreserve) { dynarray d; ConstructFromRef::UPtr p0{}; @@ -199,72 +202,66 @@ TEST_F(dynarrayTest, emplaceReferencePreserve) TEST_F(dynarrayTest, assign) { - { - double const VALUES[] = {-1.1, 0.4}; - MoveOnly src[] { MoveOnly{VALUES[0]}, - MoveOnly{VALUES[1]} }; - dynarray test; + double const VALUES[] = {-1.1, 0.4}; + MoveOnly src[] { MoveOnly{VALUES[0]}, + MoveOnly{VALUES[1]} }; + dynarray test; - test.assign(view::move(src)); + test.assign(view::move(src)); - EXPECT_EQ(2U, test.size()); - EXPECT_EQ(VALUES[0], *test[0]); - EXPECT_EQ(VALUES[1], *test[1]); + EXPECT_EQ(2U, test.size()); + EXPECT_EQ(VALUES[0], *test[0]); + EXPECT_EQ(VALUES[1], *test[1]); - test.assign(view::subrange(src, src) | view::move); - EXPECT_EQ(0U, test.size()); - } - EXPECT_EQ(MoveOnly::nConstructions, MoveOnly::nDestruct); + test.assign(view::subrange(src, src) | view::move); + EXPECT_EQ(0U, test.size()); } TEST_F(dynarrayTest, assignTrivialReloc) { + dynarray dest; + #if OEL_HAS_EXCEPTIONS { - dynarray dest; - #if OEL_HAS_EXCEPTIONS - { - TrivialRelocat obj{0}; - TrivialRelocat::countToThrowOn = 0; - EXPECT_THROW( - dest.assign(view::counted(&obj, 1)), - TestException ); - EXPECT_TRUE(dest.begin() == dest.end()); - } - #endif - EXPECT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); - - dest = {TrivialRelocat{-1.0}}; - EXPECT_EQ(1U, dest.size()); - dest = {TrivialRelocat{1.0}, TrivialRelocat{2.0}}; - EXPECT_EQ(1.0, *dest.at(0)); - EXPECT_EQ(2.0, *dest.at(1)); - EXPECT_EQ(TrivialRelocat::nConstructions - ssize(dest), TrivialRelocat::nDestruct); - #if OEL_HAS_EXCEPTIONS - { - TrivialRelocat obj{0}; - TrivialRelocat::countToThrowOn = 0; - EXPECT_THROW( - dest.assign(view::subrange(&obj, &obj + 1)), - TestException ); - EXPECT_TRUE(dest.empty() or *dest.at(1) == 2.0); - } - #endif - { - dest.clear(); - EXPECT_LE(2U, dest.capacity()); - EXPECT_TRUE(dest.empty()); - - #if OEL_HAS_EXCEPTIONS - TrivialRelocat obj{0}; - TrivialRelocat::countToThrowOn = 0; - EXPECT_THROW( - dest.assign(view::counted(&obj, 1)), - TestException ); - EXPECT_TRUE(dest.empty()); - #endif - } + TrivialRelocat obj{0}; + TrivialRelocat::countToThrowOn = 0; + EXPECT_THROW( + dest.assign(view::counted(&obj, 1)), + TestException ); + EXPECT_TRUE(dest.begin() == dest.end()); } + #endif EXPECT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); + + dest = {TrivialRelocat{-1.0}}; + EXPECT_EQ(1U, dest.size()); + dest = {TrivialRelocat{1.0}, TrivialRelocat{2.0}}; + EXPECT_EQ(1.0, *dest.at(0)); + EXPECT_EQ(2.0, *dest.at(1)); + EXPECT_EQ(TrivialRelocat::nConstructions - ssize(dest), TrivialRelocat::nDestruct); + #if OEL_HAS_EXCEPTIONS + { + TrivialRelocat obj{0}; + TrivialRelocat::countToThrowOn = 0; + EXPECT_THROW( + dest.assign(view::subrange(&obj, &obj + 1)), + TestException ); + EXPECT_TRUE(dest.empty() or *dest.at(1) == 2.0); + } + #endif + { + dest.clear(); + EXPECT_LE(2U, dest.capacity()); + EXPECT_TRUE(dest.empty()); + + #if OEL_HAS_EXCEPTIONS + TrivialRelocat obj{0}; + TrivialRelocat::countToThrowOn = 0; + EXPECT_THROW( + dest.assign(view::counted(&obj, 1)), + TestException ); + EXPECT_TRUE(dest.empty()); + #endif + } } // std::stringstream doesn't seem to work using libstdc++ with -fno-exceptions @@ -272,55 +269,52 @@ TEST_F(dynarrayTest, assignTrivialReloc) #if !defined __GLIBCXX__ or OEL_HAS_EXCEPTIONS TEST_F(dynarrayTest, assignNonForwardRange) { - { - dynarrayTrackingAlloc das; + dynarrayTrackingAlloc das; - std::string * p = nullptr; - das.assign(view::subrange(p, p)); + std::string * p = nullptr; + das.assign(view::subrange(p, p)); - EXPECT_EQ(0U, das.size()); + EXPECT_EQ(0U, das.size()); - std::stringstream ss{"My computer emits Hawking radiation"}; - std::istream_iterator b{ss}, e; - das.assign(view::subrange(b, e)); + std::stringstream ss{"My computer emits Hawking radiation"}; + std::istream_iterator b{ss}, e; + das.assign(view::subrange(b, e)); - EXPECT_EQ(5U, das.size()); + EXPECT_EQ(5U, das.size()); - EXPECT_EQ("My", das.at(0)); - EXPECT_EQ("computer", das.at(1)); - EXPECT_EQ("emits", das.at(2)); - EXPECT_EQ("Hawking", das.at(3)); - EXPECT_EQ("radiation", das.at(4)); + EXPECT_EQ("My", das.at(0)); + EXPECT_EQ("computer", das.at(1)); + EXPECT_EQ("emits", das.at(2)); + EXPECT_EQ("Hawking", das.at(3)); + EXPECT_EQ("radiation", das.at(4)); - decltype(das) copyDest; + decltype(das) copyDest; - copyDest.assign(view::counted(das.cbegin(), 2)); - copyDest.assign( view::counted(begin(das), das.size()) ); + copyDest.assign(view::counted(das.cbegin(), 2)); + copyDest.assign( view::counted(begin(das), das.size()) ); - EXPECT_TRUE(das == copyDest); + EXPECT_TRUE(das == copyDest); - copyDest.assign(view::subrange(das.cbegin(), das.cbegin() + 1)); + copyDest.assign(view::subrange(das.cbegin(), das.cbegin() + 1)); - EXPECT_EQ(1U, copyDest.size()); - EXPECT_EQ(das[0], copyDest[0]); + EXPECT_EQ(1U, copyDest.size()); + EXPECT_EQ(das[0], copyDest[0]); - copyDest.assign(view::counted(das.cbegin() + 2, 3)); + copyDest.assign(view::counted(das.cbegin() + 2, 3)); - EXPECT_EQ(3U, copyDest.size()); - EXPECT_EQ(das[2], copyDest[0]); - EXPECT_EQ(das[3], copyDest[1]); - EXPECT_EQ(das[4], copyDest[2]); + EXPECT_EQ(3U, copyDest.size()); + EXPECT_EQ(das[2], copyDest[0]); + EXPECT_EQ(das[3], copyDest[1]); + EXPECT_EQ(das[4], copyDest[2]); - copyDest = {std::string()}; - EXPECT_EQ("", copyDest.at(0)); - copyDest = {das[0], das[4]}; - EXPECT_EQ(2U, copyDest.size()); - EXPECT_EQ(das[4], copyDest.at(1)); + copyDest = {std::string()}; + EXPECT_EQ("", copyDest.at(0)); + copyDest = {das[0], das[4]}; + EXPECT_EQ(2U, copyDest.size()); + EXPECT_EQ(das[4], copyDest.at(1)); - copyDest = std::initializer_list{}; - EXPECT_TRUE(copyDest.empty()); - } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + copyDest = std::initializer_list{}; + EXPECT_TRUE(copyDest.empty()); } #endif @@ -384,25 +378,22 @@ TEST_F(dynarrayTest, appendSizeOverflow) #if !defined __GLIBCXX__ or OEL_HAS_EXCEPTIONS TEST_F(dynarrayTest, appendNonForwardRange) { - { - std::stringstream ss("1 2 3"); + std::stringstream ss("1 2 3"); - dynarrayTrackingAlloc dest; + dynarrayTrackingAlloc dest; - std::istream_iterator it(ss), end{}; + std::istream_iterator it(ss), end{}; - it = dest.append(view::counted(it, 0)); - it = dest.append(view::counted(it, 2)); - it = dest.append(view::counted(it, 0)); + it = dest.append(view::counted(it, 0)); + it = dest.append(view::counted(it, 2)); + it = dest.append(view::counted(it, 0)); - it = dest.append(view::subrange(it, end)); + it = dest.append(view::subrange(it, end)); - EXPECT_EQ(it, end); - EXPECT_EQ(3u, dest.size()); - for (int i = 0; i < 3; ++i) - EXPECT_EQ(i + 1, dest[i]); - } - ASSERT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + EXPECT_EQ(it, end); + EXPECT_EQ(3u, dest.size()); + for (int i = 0; i < 3; ++i) + EXPECT_EQ(i + 1, dest[i]); } #endif @@ -442,7 +433,7 @@ TEST_F(dynarrayTest, insertRTrivial) EXPECT_EQ(2, dest.back()); } } - EXPECT_EQ(AllocCounter::nAllocations, AllocCounter::nDeallocations); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); } } @@ -495,51 +486,97 @@ TEST_F(dynarrayTest, insertR) } } -TEST_F(dynarrayTest, insert) +TEST_F(dynarrayTest, emplace) { - { - dynarray test; + ptrdiff_t const initSize{2}; + double const firstVal {9}; + double const secondVal{7.5}; + for (auto const nReserve : {initSize, initSize + 1}) + for (ptrdiff_t insertOffset = 0; insertOffset <= initSize; ++insertOffset) + for (auto const constructThrowOnCount : {0, 1}) + for (auto const allocThrowOnCount : {0, 1}) + { + { TrivialRelocat::countToThrowOn = -1; + g_allocCount.countToThrowOn = -1; + + dynarrayTrackingAlloc dest(oel::reserve, nReserve); + dest.emplace(dest.begin(), firstVal); + dest.emplace(dest.begin(), secondVal); + + TrivialRelocat::countToThrowOn = constructThrowOnCount; + g_allocCount.countToThrowOn = allocThrowOnCount; + + if (constructThrowOnCount == 0 or (allocThrowOnCount == 0 and initSize == nReserve)) + { + #if OEL_HAS_EXCEPTIONS + EXPECT_THROW( dest.emplace(dest.begin() + insertOffset), TestException ); + + EXPECT_EQ(initSize, ssize(dest)); + #endif + } + else + { dest.emplace(dest.begin() + insertOffset); + + EXPECT_EQ(initSize + 1, ssize(dest)); + EXPECT_FALSE( dest.at(insertOffset).hasValue() ); + } + if (insertOffset == 0) + { + EXPECT_EQ(firstVal, **(dest.end() - 1)); + EXPECT_EQ(secondVal, **(dest.end() - 2)); + } + else if (insertOffset == initSize) + { + EXPECT_EQ(secondVal, *dest[0]); + EXPECT_EQ(firstVal, *dest[1]); + } + else + { EXPECT_EQ(secondVal, *dest.front()); + EXPECT_EQ(firstVal, *dest.back()); + } + } + EXPECT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); + EXPECT_EQ(g_allocCount.nAllocations, g_allocCount.nDeallocations); + } +} - double const VALUES[] = {-1.1, 0.4, 1.3, 2.2}; +TEST_F(dynarrayTest, insertTrivialAndCheckReturn) +{ + dynarray test; - auto & ptr = *test.emplace(begin(test), VALUES[2]); - EXPECT_EQ(VALUES[2], *ptr); - ASSERT_EQ(1U, test.size()); + int const values[]{-3, -1, 7, 8}; - #if OEL_HAS_EXCEPTIONS - TrivialRelocat::countToThrowOn = 0; - EXPECT_THROW( test.insert(begin(test), TrivialRelocat{0.0}), TestException ); - ASSERT_EQ(1U, test.size()); - #endif - test.insert(begin(test), TrivialRelocat{VALUES[0]}); - ASSERT_EQ(2U, test.size()); + auto it = test.insert(begin(test), values[2]); + EXPECT_EQ(test.data(), &*it); - #if OEL_HAS_EXCEPTIONS - TrivialRelocat::countToThrowOn = 0; - EXPECT_THROW( test.insert(begin(test) + 1, TrivialRelocat{0.0}), TestException ); - ASSERT_EQ(2U, test.size()); - #endif - test.emplace(end(test), VALUES[3]); - auto & p2 = *test.insert(begin(test) + 1, TrivialRelocat{VALUES[1]}); - EXPECT_EQ(VALUES[1], *p2); - ASSERT_EQ(4U, test.size()); + it = test.insert(begin(test), values[0]); + EXPECT_EQ(&test.front(), &*it); - auto v = std::begin(VALUES); - for (const auto & p : test) - { - EXPECT_EQ(*v, *p); - ++v; - } + it = test.insert(end(test), values[3]); + EXPECT_EQ(&test.back(), &*it); + + it = test.insert(begin(test) + 1, values[1]); + EXPECT_EQ(&test[1], &*it); + + EXPECT_TRUE( std::equal(std::begin(values), std::end(values), begin(test), end(test)) ); +} + +TEST_F(dynarrayTest, insertRefFromSelf) +{ + { dynarrayTrackingAlloc test; + + test.emplace(begin(test), 7); + + test.insert(begin(test), test.back()); + + EXPECT_EQ(7, *test.front()); - auto it = test.insert( begin(test) + 2, std::move(test[2]) ); - EXPECT_EQ(&test[2], &*it); + test.back() = TrivialRelocat{8}; + test.insert(begin(test), test.back()); - auto const val = *test.back(); - test.insert( end(test) - 1, std::move(test.back()) ); - ASSERT_EQ(6U, test.size()); - EXPECT_EQ(val, *end(test)[-2]); + EXPECT_EQ(8, *test.front()); } - EXPECT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); + EXPECT_EQ(TrivialRelocat::nConstructions - 1, g_allocCount.nConstructCalls); } TEST_F(dynarrayTest, mutableBeginSizeRange) @@ -735,13 +772,11 @@ TEST_F(dynarrayTest, eraseSingleInt) TEST_F(dynarrayTest, eraseSingleTrivialReloc) { testEraseOne(); - EXPECT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); } TEST_F(dynarrayTest, eraseSingle) { testEraseOne(); - EXPECT_EQ(MoveOnly::nConstructions, MoveOnly::nDestruct); } template @@ -769,13 +804,11 @@ TEST_F(dynarrayTest, eraseRangeInt) TEST_F(dynarrayTest, eraseRangeTrivialReloc) { testErase(); - EXPECT_EQ(TrivialRelocat::nConstructions, TrivialRelocat::nDestruct); } TEST_F(dynarrayTest, eraseRange) { testErase(); - EXPECT_EQ(MoveOnly::nConstructions, MoveOnly::nDestruct); } TEST_F(dynarrayTest, eraseToEnd) @@ -805,26 +838,22 @@ TEST_F(dynarrayTest, unorderErasePrecondCheck) template< typename T > void testUnorderedErase() { - T::clearCount(); - { - dynarray d; - d.emplace_back(1); - d.emplace_back(-2); - - auto it = d.unordered_erase(d.begin()); - EXPECT_EQ(1U, d.size()); - EXPECT_EQ(-2, *(*it)); - it = d.unordered_erase(it); - EXPECT_EQ(end(d), it); - - d.emplace_back(-1); - d.emplace_back(2); - unordered_erase(d, 1); - EXPECT_EQ(-1, *d.back()); - unordered_erase(d, 0); - EXPECT_TRUE(d.empty()); - } - EXPECT_EQ(T::nConstructions, T::nDestruct); + dynarray d; + d.emplace_back(1); + d.emplace_back(-2); + + auto it = d.unordered_erase(d.begin()); + EXPECT_EQ(1U, d.size()); + EXPECT_EQ(-2, *(*it)); + it = d.unordered_erase(it); + EXPECT_EQ(end(d), it); + + d.emplace_back(-1); + d.emplace_back(2); + unordered_erase(d, 1); + EXPECT_EQ(-1, *d.back()); + unordered_erase(d, 0); + EXPECT_TRUE(d.empty()); } TEST_F(dynarrayTest, unorderedErase) @@ -839,16 +868,13 @@ TEST_F(dynarrayTest, unorderedEraseTrivialReloc) TEST_F(dynarrayTest, shrinkToFit) { - { - dynarray d(oel::reserve, 9); - d.emplace_back(-5); + dynarray d(oel::reserve, 9); + d.emplace_back(-5); - d.shrink_to_fit(); + d.shrink_to_fit(); - EXPECT_GT(9u, d.capacity()); - EXPECT_EQ(1u, d.size()); - } - EXPECT_EQ(MoveOnly::nConstructions, MoveOnly::nDestruct); + EXPECT_GT(9u, d.capacity()); + EXPECT_EQ(1u, d.size()); } TEST_F(dynarrayTest, overAligned) diff --git a/unit_test/test_classes.h b/unit_test/test_classes.h index 52682327..7af70ec2 100644 --- a/unit_test/test_classes.h +++ b/unit_test/test_classes.h @@ -135,9 +135,10 @@ class TrivialRelocat : public MyCounter { conditionalThrow(); ++nConstructions; } - explicit TrivialRelocat(double v) noexcept - : val(new double{v}) - { ++nConstructions; + explicit TrivialRelocat(double v) + : val{( conditionalThrow(), new double{v} )} + { + ++nConstructions; } TrivialRelocat(const TrivialRelocat & other) @@ -186,23 +187,35 @@ struct NontrivialConstruct : MyCounter static_assert( !std::is_trivially_default_constructible::value ); -struct AllocCounter +struct TrackingAllocData { - static int nAllocations; - static int nDeallocations; - static int nConstructCalls; + int nAllocations; + int nDeallocations; + int nConstructCalls; + int countToThrowOn = -1; - static std::unordered_map sizeFromPtr; + std::unordered_map sizeFromPtr; - static void clearAll() + void clear() { nAllocations = 0; nDeallocations = 0; nConstructCalls = 0; + countToThrowOn = -1; sizeFromPtr.clear(); } + + void conditionalThrow() + { + if (0 <= countToThrowOn) + { + if (0 == countToThrowOn--) + OEL_THROW(TestException{}, ""); + } + } }; +extern TrackingAllocData g_allocCount; template struct TrackingAllocatorBase : oel::allocator @@ -213,35 +226,39 @@ struct TrackingAllocatorBase : oel::allocator T * allocate(size_type count) { + g_allocCount.conditionalThrow(); + auto const p = _base::allocate(count); if (p) - ++AllocCounter::nAllocations; + ++g_allocCount.nAllocations; - AllocCounter::sizeFromPtr[p] = count; + g_allocCount.sizeFromPtr[p] = count; return p; } T * reallocate(T * ptr, size_type count) { + g_allocCount.conditionalThrow(); + if (ptr) - ++AllocCounter::nDeallocations; + ++g_allocCount.nDeallocations; - ++AllocCounter::nAllocations; + ++g_allocCount.nAllocations; ptr = _base::reallocate(ptr, count); - AllocCounter::sizeFromPtr[ptr] = count; + g_allocCount.sizeFromPtr[ptr] = count; return ptr; } void deallocate(T * ptr, size_type count) { if (ptr) - ++AllocCounter::nDeallocations; + ++g_allocCount.nDeallocations; // verify that count matches earlier call to allocate - auto it = AllocCounter::sizeFromPtr.find(ptr); - ASSERT_TRUE(it != AllocCounter::sizeFromPtr.end()); + auto it = g_allocCount.sizeFromPtr.find(ptr); + ASSERT_TRUE(it != g_allocCount.sizeFromPtr.end()); EXPECT_EQ(it->second, count); - AllocCounter::sizeFromPtr.erase(it); + g_allocCount.sizeFromPtr.erase(it); _base::deallocate(ptr, count); } @@ -253,7 +270,7 @@ struct TrackingAllocator : TrackingAllocatorBase template void construct(U * raw, Args &&... args) { - ++AllocCounter::nConstructCalls; + ++g_allocCount.nConstructCalls; new(raw) T(std::forward(args)...);; }