diff --git a/include/boost/container/vector.hpp b/include/boost/container/vector.hpp index e0d9dd5..1f40f7c 100644 --- a/include/boost/container/vector.hpp +++ b/include/boost/container/vector.hpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -1376,8 +1377,8 @@ class vector template void emplace_back(Args &&...args) { - T* back_pos = container_detail::to_raw_pointer(this->m_holder.start()) + this->m_holder.m_size; if (this->m_holder.m_size < this->m_holder.capacity()){ + T* const back_pos = container_detail::to_raw_pointer(this->m_holder.start()) + this->m_holder.m_size; //There is more memory, just construct a new object at the end allocator_traits_type::construct(this->m_holder.alloc(), back_pos, ::boost::forward(args)...); ++this->m_holder.m_size; @@ -1414,7 +1415,7 @@ class vector BOOST_PP_EXPR_IF(n, template<) BOOST_PP_ENUM_PARAMS(n, class P) BOOST_PP_EXPR_IF(n, >) \ void emplace_back(BOOST_PP_ENUM(n, BOOST_CONTAINER_PP_PARAM_LIST, _)) \ { \ - T* back_pos = container_detail::to_raw_pointer \ + T* const back_pos = container_detail::to_raw_pointer \ (this->m_holder.start()) + this->m_holder.m_size; \ if (this->m_holder.m_size < this->m_holder.capacity()){ \ allocator_traits_type::construct (this->m_holder.alloc() \ @@ -1723,23 +1724,143 @@ class vector { x.swap(y); } #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + //! Effects: If n is less than or equal to capacity(), this call has no + //! effect. Otherwise, it is a request for allocation of additional memory + //! (memory expansion) that will not invalidate iterators. + //! If the request is successful, then capacity() is greater than or equal to + //! n; otherwise, capacity() is unchanged. In either case, size() is unchanged. + //! + //! Throws: If memory allocation allocation throws or T's copy/move constructor throws. + //! + //! Note: Non-standard extension. + bool stable_reserve(size_type new_cap) + { + const bool room_enough = this->capacity() < new_cap; + if(!room_enough && alloc_version::value < 2){ + return false; + } + else{ + //There is not enough memory, try to expand the old one + size_type real_cap = 0; + std::pair ret = this->m_holder.allocation_command + (expand_fwd, new_cap, new_cap, real_cap, this->m_holder.start()); + //Check for forward expansion + if(ret.second){ + #ifdef BOOST_CONTAINER_VECTOR_ALLOC_STATS + ++this->num_expand_fwd; + #endif + this->m_holder.capacity(real_cap); + } + return ret.second; + } + } //Absolutely experimental. This function might change, disappear or simply crash! template - void insert_ordered_at(size_type element_count, BiDirPosConstIt last_position_it, BiDirValueIt last_value_it) + void insert_ordered_at(const size_type element_count, BiDirPosConstIt last_position_it, BiDirValueIt last_value_it) { - const size_type *dummy = 0; - this->priv_insert_ordered_at(element_count, last_position_it, false, dummy, last_value_it); + const size_type old_size_pos = this->size(); + this->reserve(old_size_pos + element_count); + T* const begin_ptr = container_detail::to_raw_pointer(this->m_holder.start()); + size_type insertions_left = element_count; + size_type next_pos = old_size_pos; + size_type hole_size = element_count; + + //Exception rollback. If any copy throws before the hole is filled, values + //already inserted/copied at the end of the buffer will be destroyed. + typename value_traits::ArrayDestructor past_hole_values_destroyer + (begin_ptr + old_size_pos + element_count, this->m_holder.alloc(), size_type(0u)); + //Loop for each insertion backwards, first moving the elements after the insertion point, + //then inserting the element. + while(insertions_left){ + size_type pos = static_cast(*(--last_position_it)); + while(pos == size_type(-1)){ + --last_value_it; + pos = static_cast(*(--last_position_it)); + } + + BOOST_ASSERT(pos != size_type(-1) && pos <= old_size_pos); + //If needed shift the range after the insertion point and the previous insertion point. + //Function will take care if the shift crosses the size() boundary, using copy/move + //or uninitialized copy/move if necessary. + size_type new_hole_size = (pos != next_pos) + ? priv_insert_ordered_at_shift_range(pos, next_pos, this->size(), insertions_left) + : hole_size + ; + if(new_hole_size > 0){ + //The hole was reduced by priv_insert_ordered_at_shift_range so expand exception rollback range backwards + past_hole_values_destroyer.increment_size_backwards(next_pos - pos); + //Insert the new value in the hole + allocator_traits_type::construct(this->m_holder.alloc(), begin_ptr + pos + insertions_left - 1, *(--last_value_it)); + --new_hole_size; + if(new_hole_size == 0){ + //Hole was just filled, disable exception rollback and change vector size + past_hole_values_destroyer.release(); + this->m_holder.m_size += element_count; + } + else{ + //The hole was reduced by the new insertion by one + past_hole_values_destroyer.increment_size_backwards(size_type(1u)); + } + } + else{ + if(hole_size){ + //Hole was just filled by priv_insert_ordered_at_shift_range, disable exception rollback and change vector size + past_hole_values_destroyer.release(); + this->m_holder.m_size += element_count; + } + //Insert the new value in the already constructed range + begin_ptr[pos + insertions_left - 1] = *(--last_value_it); + } + --insertions_left; + hole_size = new_hole_size; + next_pos = pos; + } } - //Absolutely experimental. This function might change, disappear or simply crash! - template - void insert_ordered_at( size_type element_count, BiDirPosConstIt last_position_it - , BiDirSkipConstIt last_skip_it, BiDirValueIt last_value_it) + #if defined(BOOST_CONTAINER_PERFECT_FORWARDING) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Effects: Inserts an object of type T constructed with + //! std::forward(args)... in the end of the vector. + //! + //! Throws: If memory allocation throws or the in-place constructor throws or + //! T's copy/move constructor throws. + //! + //! Complexity: Amortized constant time. + template + bool stable_emplace_back(Args &&...args) { - this->priv_insert_ordered_at(element_count, last_position_it, true, last_skip_it, last_value_it); + const bool room_enough = this->m_holder.m_size < this->m_holder.capacity(); + if (room_enough){ + T* const back_pos = container_detail::to_raw_pointer(this->m_holder.start()) + this->m_holder.m_size; + //There is more memory, just construct a new object at the end + allocator_traits_type::construct(this->m_holder.alloc(), back_pos, ::boost::forward(args)...); + ++this->m_holder.m_size; + } + return room_enough; } + #else + + #define BOOST_PP_LOCAL_MACRO(n) \ + BOOST_PP_EXPR_IF(n, template<) BOOST_PP_ENUM_PARAMS(n, class P) BOOST_PP_EXPR_IF(n, >) \ + bool stable_emplace_back(BOOST_PP_ENUM(n, BOOST_CONTAINER_PP_PARAM_LIST, _)) \ + { \ + const bool room_enough = this->m_holder.m_size < this->m_holder.capacity(); \ + if (room_enough){ \ + T* const back_pos = container_detail::to_raw_pointer \ + (this->m_holder.start()) + this->m_holder.m_size; \ + allocator_traits_type::construct (this->m_holder.alloc() \ + , back_pos BOOST_PP_ENUM_TRAILING(n, BOOST_CONTAINER_PP_PARAM_FORWARD, _) ); \ + ++this->m_holder.m_size; \ + } \ + return room_enough; \ + } \ + //! + #define BOOST_PP_LOCAL_LIMITS (0, BOOST_CONTAINER_MAX_CONSTRUCTOR_PARAMETERS) + #include BOOST_PP_LOCAL_ITERATE() + + #endif //#ifdef BOOST_CONTAINER_PERFECT_FORWARDING + private: template @@ -2506,7 +2627,7 @@ class vector (this->m_holder.alloc(), pos, raw_gap, new_start + before_plus_new); //Now we have a contiguous buffer so program trailing element destruction //and update size to the final size. - old_values_destroyer.shrink_forward(elemsbefore + raw_gap); + old_values_destroyer.shrink_forward(new_size-s_before); this->m_holder.m_size = new_size; //Now move remaining last objects in the old buffer begin ::boost::move(pos + raw_gap, old_finish, old_start); diff --git a/test/expand_bwd_test_template.hpp b/test/expand_bwd_test_template.hpp index 98c30f0..237a742 100644 --- a/test/expand_bwd_test_template.hpp +++ b/test/expand_bwd_test_template.hpp @@ -17,40 +17,71 @@ #include "expand_bwd_test_allocator.hpp" #include #include +#include namespace boost { namespace container { namespace test { template struct value_holder { - value_holder(T val) : m_value(val){} - value_holder(): m_value(0){} - ~value_holder(){ m_value = 0; } + explicit value_holder(T val) : m_value(val) + { ++count; } + + value_holder(): m_value(0) + { ++count; } + + value_holder(const value_holder &other) + : m_value(other.m_value) + { ++count; } + + value_holder &operator=(const value_holder&other) + { m_value = other.m_value; return *this; } + + value_holder &operator=(T val) + { m_value = val; return *this; } + + ~value_holder() + { m_value = 0; --count; } + bool operator == (const value_holder &other) const { return m_value == other.m_value; } bool operator != (const value_holder &other) const { return m_value != other.m_value; } T m_value; + static unsigned count; }; +template +unsigned value_holder::count = 0; + template struct triple_value_holder { - triple_value_holder(T val) + explicit triple_value_holder(T val) : m_value1(val) , m_value2(val) , m_value3(val) - {} + { ++count; } triple_value_holder() : m_value1(0) , m_value2(0) , m_value3(0) - {} + { ++count; } + + triple_value_holder(const triple_value_holder &other) + : m_value1(other.m_value1), m_value2(other.m_value2), m_value3(other.m_value3) + { ++count; } + + triple_value_holder &operator=(const triple_value_holder &other) + { m_value1 = other.m_value1; m_value2 = other.m_value2; m_value3 = other.m_value3; ; return *this; } + + triple_value_holder &operator=(T val) + { m_value1 = val; m_value2 = val; m_value3 = val; ; return *this; } ~triple_value_holder() - { m_value1 = m_value2 = m_value3 = 0; } + { m_value1 = m_value2 = m_value3 = 0; --count; } bool operator == (const triple_value_holder &other) const { @@ -69,12 +100,34 @@ struct triple_value_holder T m_value1; T m_value2; T m_value3; + static unsigned count; }; +template +unsigned triple_value_holder::count = 0; + typedef value_holder int_holder; typedef triple_value_holder triple_int_holder; +template +struct life_count +{ + static unsigned check(unsigned) { return true; } +}; +template +struct life_count< triple_value_holder > +{ + static unsigned check(unsigned c) + { return c == triple_value_holder::count; } +}; + +template +struct life_count< value_holder > +{ + static unsigned check(unsigned c) + { return c == value_holder::count; } +}; //Function to check if both sets are equal template @@ -134,43 +187,46 @@ bool test_insert_with_expand_bwd() for(int iteration = 0; iteration < Iterations; ++iteration) { - value_type *memory = new value_type[MemorySize]; - BOOST_TRY { - std::vector initial_data; - initial_data.resize(InitialSize[iteration]); - for(int i = 0; i < InitialSize[iteration]; ++i){ - initial_data[i] = i; - } - - Vect data_to_insert; - data_to_insert.resize(InsertSize[iteration]); - for(int i = 0; i < InsertSize[iteration]; ++i){ - data_to_insert[i] = -i; - } - - expand_bwd_test_allocator alloc - (&memory[0], MemorySize, Offset[iteration]); - VectorWithExpandBwdAllocator vector(alloc); - vector.insert( vector.begin() - , initial_data.begin(), initial_data.end()); - vector.insert( vector.begin() + Position[iteration] - , data_to_insert.begin(), data_to_insert.end()); - initial_data.insert(initial_data.begin() + Position[iteration] - , data_to_insert.begin(), data_to_insert.end()); - //Now check that values are equal - if(!CheckEqualVector(vector, initial_data)){ - std::cout << "test_assign_with_expand_bwd::CheckEqualVector failed." << std::endl - << " Class: " << typeid(VectorWithExpandBwdAllocator).name() << std::endl - << " Iteration: " << iteration << std::endl; - return false; - } + boost::movelib::unique_ptr memptr = + boost::movelib::make_unique_definit(MemorySize*sizeof(value_type)); + value_type *memory = (value_type*)memptr.get(); + std::vector initial_data; + initial_data.resize(InitialSize[iteration]); + for(int i = 0; i < InitialSize[iteration]; ++i){ + initial_data[i] = i; } - BOOST_CATCH(...){ - delete [](const_cast(memory)); - BOOST_RETHROW; + + if(!life_count::check(InitialSize[iteration])) + return false; + Vect data_to_insert; + data_to_insert.resize(InsertSize[iteration]); + for(int i = 0; i < InsertSize[iteration]; ++i){ + data_to_insert[i] = -i; + } + + if(!life_count::check(InitialSize[iteration]+InsertSize[iteration])) + return false; + + expand_bwd_test_allocator alloc + (&memory[0], MemorySize, Offset[iteration]); + VectorWithExpandBwdAllocator vector(alloc); + vector.insert( vector.begin() + , initial_data.begin(), initial_data.end()); + vector.insert( vector.begin() + Position[iteration] + , data_to_insert.begin(), data_to_insert.end()); + + if(!life_count::check(InitialSize[iteration]*2+InsertSize[iteration]*2)) + return false; + + initial_data.insert(initial_data.begin() + Position[iteration] + , data_to_insert.begin(), data_to_insert.end()); + //Now check that values are equal + if(!CheckEqualVector(vector, initial_data)){ + std::cout << "test_assign_with_expand_bwd::CheckEqualVector failed." << std::endl + << " Class: " << typeid(VectorWithExpandBwdAllocator).name() << std::endl + << " Iteration: " << iteration << std::endl; + return false; } - BOOST_CATCH_END - delete [](const_cast(memory)); } return true;