From 0abb5851f234a012ea7b0b077ec1e9696213eeca Mon Sep 17 00:00:00 2001 From: Alexander van Gessel Date: Sat, 11 Nov 2017 00:14:51 +0100 Subject: [PATCH 1/4] Add emplace_{back,front} to circular_buffer --- include/boost/circular_buffer/base.hpp | 156 +++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/include/boost/circular_buffer/base.hpp b/include/boost/circular_buffer/base.hpp index 47441544..675658b6 100644 --- a/include/boost/circular_buffer/base.hpp +++ b/include/boost/circular_buffer/base.hpp @@ -1454,6 +1454,82 @@ private empty_value BOOST_CATCH_END } +#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES + template + void emplace_back_impl(BOOST_FWD_REF(Args) ...args) { + if (full()) { + if (empty()) + return; + replace(m_last, ::boost::forward(args)...); + increment(m_last); + m_first = m_last; + } else { + boost::allocator_construct(alloc(), boost::to_address(m_last), ::boost::forward(args)...); + increment(m_last); + ++m_size; + } + } +#else + template + void emplace_back_impl(BOOST_FWD_REF(V) value) { + if (full()) { + if (empty()) + return; + replace(m_last, ::boost::forward(value)); + increment(m_last); + m_first = m_last; + } else { + boost::allocator_construct(alloc(), boost::to_address(m_last), ::boost::forward(value)); + increment(m_last); + ++m_size; + } + } +#endif + +#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES + template + void emplace_front_impl(BOOST_FWD_REF(Args) ...args) { + BOOST_TRY { + if (full()) { + if (empty()) + return; + decrement(m_first); + replace(m_first, ::boost::forward(args)...); + m_last = m_first; + } else { + decrement(m_first); + boost::allocator_construct(alloc(), boost::to_address(m_first), ::boost::forward(args)...); + ++m_size; + } + } BOOST_CATCH(...) { + increment(m_first); + BOOST_RETHROW + } + BOOST_CATCH_END + } +#else + template + void emplace_front_impl(BOOST_FWD_REF(V) value) { + BOOST_TRY { + if (full()) { + if (empty()) + return; + decrement(m_first); + replace(m_first, ::boost::forward(value)); + m_last = m_first; + } else { + decrement(m_first); + boost::allocator_construct(alloc(), boost::to_address(m_first), ::boost::forward(value)); + ++m_size; + } + } BOOST_CATCH(...) { + increment(m_first); + BOOST_RETHROW + } + BOOST_CATCH_END + } +#endif + public: //! Insert a new element at the end of the circular_buffer. /*! @@ -1583,6 +1659,64 @@ private empty_value push_front(boost::move(temp)); } + //! Construct a new element at the end of the circular_buffer. + /*! + \post if capacity() > 0 then back() == item
+ If the circular_buffer is full, the first element will be removed. If the capacity is + 0, nothing will be inserted. + \param item The element to be inserted. + \throws Whatever T::T(Args...) throws. + Whatever T::operator = (T&&) throws. + \par Exception Safety + Basic; no-throw if the operation in the Throws section does not throw anything. + \par Iterator Invalidation + Does not invalidate any iterators with the exception of iterators pointing to the overwritten element. + \par Complexity + Constant (in the size of the circular_buffer). + \sa \link push_back() push_back(const_reference)\endlink, + pop_back(), emplace_front() + */ +#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES + template + void emplace_back(BOOST_FWD_REF(Args) ...args) { + emplace_back_impl(::boost::forward(args)...); + } +#else + template + void emplace_back(BOOST_FWD_REF(V) value) { + emplace_back_impl(::boost::forward(value)); + } +#endif + + //! Construct a new element at the beginning of the circular_buffer. + /*! + \post if capacity() > 0 then back() == item
+ If the circular_buffer is full, the last element will be removed. If the capacity is + 0, nothing will be inserted. + \param item The element to be inserted. + \throws Whatever T::T(Args...) throws. + Whatever T::operator = (T&&) throws. + \par Exception Safety + Basic; no-throw if the operation in the Throws section does not throw anything. + \par Iterator Invalidation + Does not invalidate any iterators with the exception of iterators pointing to the overwritten element. + \par Complexity + Constant (in the size of the circular_buffer). + \sa \link push_front() push_front(const_reference)\endlink, + pop_front(), emplace_back() + */ +#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES + template + void emplace_front(BOOST_FWD_REF(Args) ...args) { + emplace_front_impl(::boost::forward(args)...); + } +#else + template + void emplace_front(BOOST_FWD_REF(V) value) { + emplace_front_impl(::boost::forward(value)); + } +#endif + //! Remove the last element from the circular_buffer. /*! \pre !empty() @@ -2426,6 +2560,28 @@ private empty_value #endif } +#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES + /*! INTERNAL ONLY */ + template + void replace(pointer pos, BOOST_FWD_REF(Args) ...args) { + boost::allocator_destroy(alloc(), boost::to_address(pos)); + boost::allocator_construct(alloc(), boost::to_address(pos), ::boost::forward(args)...); +#if BOOST_CB_ENABLE_DEBUG + invalidate_iterators(iterator(this, pos)); +#endif + } +#else /* BOOST_NO_CXX11_VARIADIC_TEMPLATES */ + /*! INTERNAL ONLY */ + template + void replace(pointer pos, BOOST_FWD_REF(V) value) { + boost::allocator_destroy(alloc(), boost::to_address(pos)); + boost::allocator_construct(alloc(), boost::to_address(pos), ::boost::forward(value)); +#if BOOST_CB_ENABLE_DEBUG + invalidate_iterators(iterator(this, pos)); +#endif + } +#endif /* BOOST_NO_CXX11_VARIADIC_TEMPLATES */ + /*! INTERNAL ONLY */ void construct_or_replace(bool construct, pointer pos, param_value_type item) { if (construct) From 88053684e1ec220fd896f53f045f92d3397d399e Mon Sep 17 00:00:00 2001 From: Alexander van Gessel Date: Sat, 11 Nov 2017 00:15:08 +0100 Subject: [PATCH 2/4] Add emplace_{back,front} to circular_buffer_space_optimized --- .../boost/circular_buffer/space_optimized.hpp | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/include/boost/circular_buffer/space_optimized.hpp b/include/boost/circular_buffer/space_optimized.hpp index 21681fd3..81613d78 100644 --- a/include/boost/circular_buffer/space_optimized.hpp +++ b/include/boost/circular_buffer/space_optimized.hpp @@ -905,6 +905,76 @@ public:
circular_buffer::push_front(); } + //! Construct a new element at the end of the space optimized circular buffer. + /*! + \post if capacity().%capacity() > 0 then back() == item
+ If the circular_buffer_space_optimized is full, the first element will be removed. If the + capacity is 0, nothing will be inserted.

+ The amount of allocated memory in the internal buffer may be predictively increased. + \param item The element to be inserted. + \throws "An allocation error" if memory is exhausted (std::bad_alloc if the standard allocator is + used). + Whatever T::T(Args...) throws. + Whatever T::operator = (T&&) throws. + \par Exception Safety + Basic. + \par Iterator Invalidation + Invalidates all iterators pointing to the circular_buffer_space_optimized (except iterators + equal to end()). + \par Complexity + Linear (in the size of the circular_buffer_space_optimized). + \sa \link push_front() push_front(const_reference)\endlink, pop_back(), + pop_front() + */ +#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES + template + void emplace_back(BOOST_FWD_REF(Args) ...args) { + check_low_capacity(); + circular_buffer::emplace_back(::boost::forward(args)...); + } +#else + template + void emplace_back(BOOST_FWD_REF(V) value) { + check_low_capacity(); + circular_buffer::emplace_back(::boost::forward(value)); + } +#endif + + //! Construct a new element at the beginning of the space optimized circular buffer. + /*! + \post if capacity().%capacity() > 0 then front() == item
+ If the circular_buffer_space_optimized is full, the last element will be removed. If the + capacity is 0, nothing will be inserted.

+ The amount of allocated memory in the internal buffer may be predictively increased. + \param item The element to be inserted. + \throws "An allocation error" if memory is exhausted (std::bad_alloc if the standard allocator is + used). + Whatever T::T(Args...) throws or nothing if T::T(T&&) is noexcept. + Whatever T::operator = (T&&) throws. + \par Exception Safety + Basic. + \par Iterator Invalidation + Invalidates all iterators pointing to the circular_buffer_space_optimized (except iterators + equal to end()). + \par Complexity + Linear (in the size of the circular_buffer_space_optimized). + \sa \link push_back() push_back(const_reference)\endlink, pop_back(), + pop_front() + */ +#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES + template + void emplace_front(BOOST_FWD_REF(Args) ...args) { + check_low_capacity(); + circular_buffer::emplace_front(::boost::forward(args)...); + } +#else + template + void emplace_front(BOOST_FWD_REF(V) value) { + check_low_capacity(); + circular_buffer::emplace_front(::boost::forward(value)); + } +#endif + //! Remove the last element from the space optimized circular buffer. /*! \pre !empty() From 0019f88b60acec334074807bfce5b5aa6399d91b Mon Sep 17 00:00:00 2001 From: Alexander van Gessel Date: Sat, 13 Oct 2018 15:13:50 +0200 Subject: [PATCH 3/4] Add tests for emplace_{back,front} --- test/common.ipp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/common.ipp b/test/common.ipp index 4d3d2790..a6e872fe 100644 --- a/test/common.ipp +++ b/test/common.ipp @@ -283,6 +283,7 @@ void cxx11_allocator_test() { CB_CONTAINER > cb(10, 0); generic_test(cb); } + #endif void begin_and_end_test() { @@ -1153,6 +1154,32 @@ void pop_back_test() { generic_test(cb); } +void emplace_test(){ + CB_CONTAINER cb(4); + cb.emplace_back(4); + cb.emplace_back(5); + cb.emplace_back(6); + cb.emplace_back(7); + + BOOST_TEST(cb.size() == 4); + BOOST_TEST(cb.front() == 4); + BOOST_TEST(cb.back() == 7); + + cb.emplace_front(3); + cb.emplace_front(2); + + BOOST_TEST(cb.front() == 2); + BOOST_TEST(cb.back() == 5); + + cb.emplace_front(1); + cb.emplace_front(0); + + BOOST_TEST(cb.size() == 4); + BOOST_TEST(*cb.begin() == 0); + BOOST_TEST(cb.front() == 0); + BOOST_TEST(cb.back() == 3); +} + void insert_test() { CB_CONTAINER cb1(4); @@ -2467,6 +2494,7 @@ void run_common_tests() swap_test(); push_back_test(); pop_back_test(); + emplace_test(); insert_test(); insert_n_test(); insert_range_test(); From 539d34cb20673b99a90e1687749cd0869bf671ae Mon Sep 17 00:00:00 2001 From: Alexander van Gessel Date: Thu, 2 Feb 2023 12:37:59 +0100 Subject: [PATCH 4/4] Add tests to verify that emplace methods do not make copies --- test/Jamfile.v2 | 1 + test/emplace_test_copies.cpp | 104 +++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 test/emplace_test_copies.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 7e1ae584..303e0c6d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -28,5 +28,6 @@ test-suite "circular_buffer" [ run space_optimized_test.cpp : : : single "BOOST_CB_ENABLE_DEBUG=1" : space_optimized_test_dbg ] [ run soft_iterator_invalidation.cpp : : : single : ] [ run constant_erase_test.cpp : : : single : ] + [ run emplace_test_copies.cpp : : : single : ] [ compile bounded_buffer_comparison.cpp : multi : ] ; diff --git a/test/emplace_test_copies.cpp b/test/emplace_test_copies.cpp new file mode 100644 index 00000000..a826c232 --- /dev/null +++ b/test/emplace_test_copies.cpp @@ -0,0 +1,104 @@ +// Tests to track that emplace does not make copies. + +// Copyright (c) 2023 Alexander van Gessel + +// Use, modification, and distribution is subject to 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) + +#include "test.hpp" + +#include + +typedef class BaseConstructDestructTracker { +public: + static size_t construct_count; + static size_t destruct_count; + + BaseConstructDestructTracker() { + ++construct_count; + } + ~BaseConstructDestructTracker() { + ++destruct_count; + } + static void reset() { + construct_count = 0; + destruct_count = 0; + } +} BCDT; + +size_t BCDT::construct_count; +size_t BCDT::destruct_count; + +class NoArgTracker : BaseConstructDestructTracker {}; +template +class TwoArgTracker : BaseConstructDestructTracker { + A1 a1; + A2 a2; + +public: + explicit TwoArgTracker(A1 a1, A2 a2) : a1(a1), a2(a2) {} +}; + +void track_base_test() { + const size_t capacity = 8; + const size_t iters = 16; + BCDT::reset(); + circular_buffer cb(capacity); + BOOST_TEST(BCDT::construct_count == 0); + BOOST_TEST(BCDT::destruct_count == 0); + for(size_t i = 1; i <= iters; ++i) { + cb.emplace_back(); + BOOST_TEST(BCDT::construct_count == i); + BOOST_TEST(BCDT::destruct_count == i - cb.size()); + } + for(size_t i = 1; i <= capacity; ++i) { + cb.pop_back(); + BOOST_TEST(BCDT::construct_count == iters); + BOOST_TEST(BCDT::destruct_count == iters - cb.size()); + } + BOOST_TEST(BCDT::construct_count == BCDT::destruct_count); + BOOST_TEST(cb.size() == 0); + BCDT::reset(); + for(size_t i = 1; i <= iters; ++i) { + cb.emplace_front(); + BOOST_TEST(BCDT::construct_count == i); + BOOST_TEST(BCDT::destruct_count == i - cb.size()); + } +} + +void track_twoarg_test() { + const size_t capacity = 8; + const size_t iters = 16; + const std::string prefix = "iteration "; + BCDT::reset(); + circular_buffer> cb(capacity); + BOOST_TEST(BCDT::construct_count == 0); + BOOST_TEST(BCDT::destruct_count == 0); + for(size_t i = 1; i <= iters; ++i) { + cb.emplace_back(i, prefix + boost::lexical_cast(i)); + BOOST_TEST(BCDT::construct_count == i); + BOOST_TEST(BCDT::destruct_count == i - cb.size()); + } + for(size_t i = 1; i <= capacity; ++i) { + cb.pop_back(); + BOOST_TEST(BCDT::construct_count == iters); + BOOST_TEST(BCDT::destruct_count == iters - cb.size()); + } + BOOST_TEST(BCDT::construct_count == BCDT::destruct_count); + BOOST_TEST(cb.size() == 0); + BCDT::reset(); + for(size_t i = 1; i <= iters; ++i) { + cb.emplace_front(i, prefix + boost::lexical_cast(i)); + BOOST_TEST(BCDT::construct_count == i); + BOOST_TEST(BCDT::destruct_count == i - cb.size()); + } +} + +// test main +int main() +{ + track_base_test(); + track_twoarg_test(); + return boost::report_errors(); +}