mirror of
https://github.com/boostorg/container.git
synced 2026-07-05 12:20:47 +02:00
Renamed "emplace" as "quick_emplace" and recover strong exception guarantee for "emplace".
This commit is contained in:
@@ -188,6 +188,41 @@ BOOST_CONTAINER_FORCEINLINE void erase_void(boost::container::nest<Args...>& x,
|
||||
x.erase_void(it);
|
||||
}
|
||||
|
||||
//quick_emplace: containers without a quick_emplace member (hub, plf::hive)
|
||||
//fall back to insert; nest uses its faster, capacity-rollback-free path.
|
||||
template<typename Container, typename T>
|
||||
BOOST_CONTAINER_FORCEINLINE typename Container::iterator
|
||||
quick_emplace(Container& x, const T& v)
|
||||
{
|
||||
return x.insert(v);
|
||||
}
|
||||
|
||||
template<typename... Args, typename T>
|
||||
BOOST_CONTAINER_FORCEINLINE typename boost::container::nest<Args...>::iterator
|
||||
quick_emplace(boost::container::nest<Args...>& x, const T& v)
|
||||
{
|
||||
return x.quick_emplace(v);
|
||||
}
|
||||
|
||||
//quick_erase: erase helper mirroring erase_void, used by the quick build path.
|
||||
template<typename Container, typename Iterator>
|
||||
BOOST_CONTAINER_FORCEINLINE void quick_erase(Container& x, Iterator it)
|
||||
{
|
||||
x.erase(it);
|
||||
}
|
||||
|
||||
template<typename... Args, typename Iterator>
|
||||
BOOST_CONTAINER_FORCEINLINE void quick_erase(boost::container::hub<Args...>& x, Iterator it)
|
||||
{
|
||||
x.erase_void(it);
|
||||
}
|
||||
|
||||
template<typename... Args, typename Iterator>
|
||||
BOOST_CONTAINER_FORCEINLINE void quick_erase(boost::container::nest<Args...>& x, Iterator it)
|
||||
{
|
||||
x.erase_void(it);
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
Container make(std::size_t n, double erasure_rate)
|
||||
{
|
||||
@@ -217,6 +252,37 @@ void fill(Container& c, std::size_t n)
|
||||
}
|
||||
}
|
||||
|
||||
//Quick variants of make/fill that exercise the quick_emplace insertion path
|
||||
//(nest::quick_emplace; insert for the other containers).
|
||||
template<typename Container>
|
||||
Container quick_make(std::size_t n, double erasure_rate)
|
||||
{
|
||||
std::uint64_t erasure_cut =
|
||||
(std::uint64_t)(erasure_rate * (double)(std::uint64_t)(-1));
|
||||
|
||||
Container c;
|
||||
urbg rng;
|
||||
std::vector<typename Container::iterator> iterators;
|
||||
|
||||
iterators.reserve(n);
|
||||
for(std::size_t i = 0; i < n; ++i) iterators.push_back(quick_emplace(c, (int)rng()));
|
||||
std::shuffle(iterators.begin(), iterators.end(), rng);
|
||||
for(auto it: iterators) {
|
||||
if(rng() < erasure_cut) quick_erase(c, it);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
void quick_fill(Container& c, std::size_t n)
|
||||
{
|
||||
urbg rng;
|
||||
if(n > c.size()) {
|
||||
n -= c.size();
|
||||
while(n--) quick_emplace(c, (int)rng());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static std::size_t min_size_exp = 3,
|
||||
max_size_exp = 7;
|
||||
@@ -393,6 +459,27 @@ struct filling
|
||||
}
|
||||
};
|
||||
|
||||
//Like filling, but the measured re-insertion uses the quick_emplace path
|
||||
//(nest::quick_emplace; insert for hub/hive).
|
||||
template<typename Container>
|
||||
struct quick_filling
|
||||
{
|
||||
unsigned int operator()(std::size_t n, double erasure_rate) const
|
||||
{
|
||||
unsigned int res = 0;
|
||||
{
|
||||
pause_timing();
|
||||
auto c = quick_make<Container>(n, erasure_rate); // excluded
|
||||
resume_timing();
|
||||
quick_fill(c, n); // measured
|
||||
res = (unsigned int)c.size();
|
||||
pause_timing(); // exclude destruction
|
||||
}
|
||||
resume_timing();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Container>
|
||||
struct erasure
|
||||
{
|
||||
@@ -602,6 +689,9 @@ run_summary run_bench()
|
||||
t.push_back(benchmark(
|
||||
"filling", element_size,
|
||||
filling<num>{}, filling<den>{}));
|
||||
t.push_back(benchmark(
|
||||
"quick filling", element_size,
|
||||
quick_filling<num>{}, quick_filling<den>{}));
|
||||
t.push_back(benchmark(
|
||||
"erasure", element_size,
|
||||
::erasure<num>{}, ::erasure<den>{}));
|
||||
|
||||
@@ -1575,9 +1575,54 @@ class nest
|
||||
//!
|
||||
//! <b>Returns</b>: An iterator to the inserted element.
|
||||
//!
|
||||
//! <b>Throws</b>: Nothing unless the element's constructor throws. If it does,
|
||||
//! the container is left in its original state (strong exception guarantee):
|
||||
//! any block speculatively allocated for the new element is freed.
|
||||
//!
|
||||
//! <b>Complexity</b>: Constant (amortized).
|
||||
template<class ...Args>
|
||||
BOOST_CONTAINER_FORCEINLINE iterator emplace(BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
block_base_pointer const pbb_prev = blist.next_available;
|
||||
int n;
|
||||
block_pointer const pb = priv_retrieve_available_block(n);
|
||||
this->priv_construct_or_restore_capacity(
|
||||
boost::movelib::to_raw_pointer(pb->data()) + n, pbb_prev,
|
||||
boost::forward<Args>(args)...);
|
||||
pb->mask |= pb->mask + 1u;
|
||||
const mask_type mask_plus_one = pb->mask + 1u;
|
||||
if (BOOST_UNLIKELY(mask_plus_one <= 2)) {
|
||||
// pb->mask == 0 (impossible), 1 or full
|
||||
if (mask_plus_one == 0) blist.unlink_available(pb);
|
||||
// pb->mask == 1
|
||||
else blist.link_at_back(pb);
|
||||
}
|
||||
|
||||
++size_;
|
||||
return iterator(pb, n);
|
||||
}
|
||||
|
||||
//! <b>Effects</b>: Inserts an element constructed in-place with args (hint ignored).
|
||||
//!
|
||||
//! <b>Returns</b>: An iterator to the inserted element.
|
||||
//!
|
||||
//! <b>Complexity</b>: Constant (amortized).
|
||||
template<class ...Args>
|
||||
BOOST_CONTAINER_FORCEINLINE iterator emplace_hint(const_iterator, BOOST_FWD_REF(Args)... args)
|
||||
{ return emplace(boost::forward<Args>(args)...); }
|
||||
|
||||
//! <b>Effects</b>: Inserts an element constructed in-place with args.
|
||||
//!
|
||||
//! <b>Returns</b>: An iterator to the inserted element.
|
||||
//!
|
||||
//! <b>Throws</b>: Nothing unless the element's constructor throws.
|
||||
//! Basic exception guarantee.
|
||||
//!
|
||||
//! <b>Complexity</b>: Constant (amortized).
|
||||
//!
|
||||
//! <b>Note</b>: Experimental API.
|
||||
template<class ...Args>
|
||||
BOOST_CONTAINER_FORCEINLINE iterator quick_emplace(BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
int n;
|
||||
block_pointer const pb = priv_retrieve_available_block(n);
|
||||
@@ -1601,20 +1646,33 @@ class nest
|
||||
return iterator(pb, n);
|
||||
}
|
||||
|
||||
//! <b>Effects</b>: Inserts an element constructed in-place with args (hint ignored).
|
||||
//!
|
||||
//! <b>Returns</b>: An iterator to the inserted element.
|
||||
//!
|
||||
//! <b>Complexity</b>: Constant (amortized).
|
||||
template<class ...Args>
|
||||
BOOST_CONTAINER_FORCEINLINE iterator emplace_hint(const_iterator, BOOST_FWD_REF(Args)... args)
|
||||
{ return emplace(boost::forward<Args>(args)...); }
|
||||
|
||||
#else // BOOST_NO_CXX11_VARIADIC_TEMPLATES
|
||||
|
||||
#define BOOST_CONTAINER_NEST_EMPLACE_CODE(N) \
|
||||
BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
|
||||
BOOST_CONTAINER_FORCEINLINE iterator emplace(BOOST_MOVE_UREF##N) \
|
||||
{ \
|
||||
block_base_pointer const pbb_prev = blist.next_available; \
|
||||
int n_; \
|
||||
block_pointer pb = priv_retrieve_available_block(n_); \
|
||||
this->priv_construct_or_restore_capacity( \
|
||||
boost::movelib::to_raw_pointer(pb->data() + n_), pbb_prev \
|
||||
BOOST_MOVE_I##N BOOST_MOVE_FWD##N); \
|
||||
const mask_type mask_plus_one = (pb->mask |= pb->mask + 1u) + 1u; \
|
||||
if (BOOST_UNLIKELY(mask_plus_one <= 2)) { \
|
||||
if (mask_plus_one == 0) blist.unlink_available(pb); \
|
||||
else blist.link_at_back(pb); \
|
||||
} \
|
||||
++size_; \
|
||||
return iterator(pb, n_); \
|
||||
} \
|
||||
\
|
||||
BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
|
||||
BOOST_CONTAINER_FORCEINLINE iterator emplace_hint(const_iterator BOOST_MOVE_I##N BOOST_MOVE_UREF##N) \
|
||||
{ return emplace(BOOST_MOVE_FWD##N); } \
|
||||
\
|
||||
BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
|
||||
BOOST_CONTAINER_FORCEINLINE iterator quick_emplace(BOOST_MOVE_UREF##N) \
|
||||
{ \
|
||||
int n_; \
|
||||
block_pointer pb = priv_retrieve_available_block(n_); \
|
||||
@@ -1629,10 +1687,6 @@ class nest
|
||||
++size_; \
|
||||
return iterator(pb, n_); \
|
||||
} \
|
||||
\
|
||||
BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
|
||||
BOOST_CONTAINER_FORCEINLINE iterator emplace_hint(const_iterator BOOST_MOVE_I##N BOOST_MOVE_UREF##N) \
|
||||
{ return emplace(BOOST_MOVE_FWD##N); } \
|
||||
//
|
||||
BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_NEST_EMPLACE_CODE)
|
||||
#undef BOOST_CONTAINER_NEST_EMPLACE_CODE
|
||||
@@ -1979,6 +2033,57 @@ class nest
|
||||
}
|
||||
}
|
||||
|
||||
// If the next_available block at the moment of failure is not the same as the one
|
||||
// observed before calling priv_retrieve_available_block, a new (and now empty) block
|
||||
// was freshly allocated for the failed construction: free it to restore capacity.
|
||||
BOOST_CONTAINER_NOINLINE void priv_restore_capacity_on_throw(block_base_pointer pbb_prev) BOOST_NOEXCEPT
|
||||
{
|
||||
if(blist.next_available != pbb_prev) {
|
||||
block_pointer const pb_new = static_cast_block_pointer(blist.next_available);
|
||||
blist.unlink_available(pb_new);
|
||||
priv_delete_block(pb_new);
|
||||
--num_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
// The try/catch lives here (not inline in emplace) on purpose: MSVC will not
|
||||
// inline a function that contains its own EH scope, so keeping emplace
|
||||
// EH-clean lets it be inlined into the caller's insert loop. This is a plain
|
||||
// (non-FORCEINLINE) helper so the EH scope stays out of emplace.
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template<class ...Args>
|
||||
inline void priv_construct_or_restore_capacity
|
||||
(T* p, block_base_pointer pbb_prev, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
BOOST_TRY{
|
||||
block_alloc_traits::construct(al(), p, boost::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
this->priv_restore_capacity_on_throw(pbb_prev);
|
||||
BOOST_RETHROW;
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
#else
|
||||
#define BOOST_CONTAINER_NEST_CONSTRUCT_OR_RESTORE_CODE(N) \
|
||||
BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
|
||||
inline void priv_construct_or_restore_capacity \
|
||||
(T* p, block_base_pointer pbb_prev BOOST_MOVE_I##N BOOST_MOVE_UREF##N) \
|
||||
{ \
|
||||
BOOST_TRY{ \
|
||||
block_alloc_traits::construct(al(), p BOOST_MOVE_I##N BOOST_MOVE_FWD##N); \
|
||||
} \
|
||||
BOOST_CATCH(...){ \
|
||||
this->priv_restore_capacity_on_throw(pbb_prev); \
|
||||
BOOST_RETHROW; \
|
||||
} \
|
||||
BOOST_CATCH_END \
|
||||
} \
|
||||
//
|
||||
BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_NEST_CONSTRUCT_OR_RESTORE_CODE)
|
||||
#undef BOOST_CONTAINER_NEST_CONSTRUCT_OR_RESTORE_CODE
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// private: destruction helpers
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
|
||||
using namespace boost::container;
|
||||
|
||||
@@ -93,6 +95,36 @@ void test_emplace()
|
||||
BOOST_TEST_EQ(h.size(), 1u);
|
||||
}
|
||||
|
||||
void test_quick_emplace()
|
||||
{
|
||||
nest<int> h;
|
||||
nest<int>::iterator it = h.quick_emplace(0);
|
||||
BOOST_TEST_EQ(*it, 0);
|
||||
BOOST_TEST_EQ(h.size(), 1u);
|
||||
|
||||
//Fill enough elements to span several blocks and verify all are present.
|
||||
const int count = 1000;
|
||||
for(int i = 1; i < count; ++i){
|
||||
nest<int>::iterator jt = h.quick_emplace(i);
|
||||
BOOST_TEST_EQ(*jt, i);
|
||||
}
|
||||
BOOST_TEST_EQ(h.size(), (std::size_t)count);
|
||||
|
||||
//quick_emplace and emplace must interoperate on the same container.
|
||||
h.emplace(-1);
|
||||
h.quick_emplace(-2);
|
||||
BOOST_TEST_EQ(h.size(), (std::size_t)(count + 2));
|
||||
|
||||
//All originally inserted values [0, count) must still be retrievable.
|
||||
std::vector<bool> seen(count, false);
|
||||
for(nest<int>::iterator b = h.begin(), e = h.end(); b != e; ++b){
|
||||
if(*b >= 0 && *b < count)
|
||||
seen[(std::size_t)*b] = true;
|
||||
}
|
||||
for(int i = 0; i < count; ++i)
|
||||
BOOST_TEST(seen[(std::size_t)i]);
|
||||
}
|
||||
|
||||
void test_assign()
|
||||
{
|
||||
nest<int> h(5, 1);
|
||||
@@ -727,6 +759,7 @@ int main()
|
||||
test_move_construction();
|
||||
test_insert_erase();
|
||||
test_emplace();
|
||||
test_quick_emplace();
|
||||
test_assign();
|
||||
test_copy_assignment();
|
||||
test_move_assignment();
|
||||
|
||||
Reference in New Issue
Block a user