Commit 2c62efc8 authored by Martin Martin's avatar Martin Martin Committed by Facebook Github Bot

NoHeap small_vector's no longer require a move constructor.

Summary:
NoHeap small_vector's no longer require a move constructor.

Also changes const -> constexpr in a bunch of places.

Reviewed By: nbronson

Differential Revision: D5804361

fbshipit-source-id: 0f00fee6d318fa9296612409805d4ffcfbadb974
parent 61017384
...@@ -26,9 +26,11 @@ ...@@ -26,9 +26,11 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include <iterator> #include <iterator>
#include <stdexcept> #include <stdexcept>
#include <type_traits> #include <type_traits>
#include <utility>
#include <boost/mpl/count.hpp> #include <boost/mpl/count.hpp>
#include <boost/mpl/empty.hpp> #include <boost/mpl/empty.hpp>
...@@ -85,74 +87,6 @@ class small_vector; ...@@ -85,74 +87,6 @@ class small_vector;
namespace detail { namespace detail {
/*
* Move a range to a range of uninitialized memory. Assumes the
* ranges don't overlap.
*/
template <class T>
typename std::enable_if<!FOLLY_IS_TRIVIALLY_COPYABLE(T)>::type
moveToUninitialized(T* first, T* last, T* out) {
std::size_t idx = 0;
try {
for (; first != last; ++first, ++idx) {
new (&out[idx]) T(std::move(*first));
}
} catch (...) {
// Even for callers trying to give the strong guarantee
// (e.g. push_back) it's ok to assume here that we don't have to
// move things back and that it was a copy constructor that
// threw: if someone throws from a move constructor the effects
// are unspecified.
for (std::size_t i = 0; i < idx; ++i) {
out[i].~T();
}
throw;
}
}
// Specialization for trivially copyable types.
template <class T>
typename std::enable_if<FOLLY_IS_TRIVIALLY_COPYABLE(T)>::type
moveToUninitialized(T* first, T* last, T* out) {
std::memmove(out, first, (last - first) * sizeof *first);
}
/*
* Move a range to a range of uninitialized memory. Assumes the
* ranges don't overlap. Inserts an element at out + pos using emplaceFunc().
* out will contain (end - begin) + 1 elements on success and none on failure.
* If emplaceFunc() throws [begin, end) is unmodified.
*/
template <class T, class Size, class EmplaceFunc>
void moveToUninitializedEmplace(
T* begin,
T* end,
T* out,
Size pos,
EmplaceFunc&& emplaceFunc) {
// Must be called first so that if it throws [begin, end) is unmodified.
// We have to support the strong exception guarantee for emplace_back().
emplaceFunc(out + pos);
// move old elements to the left of the new one
try {
detail::moveToUninitialized(begin, begin + pos, out);
} catch (...) {
out[pos].~T();
throw;
}
// move old elements to the right of the new one
try {
if (begin + pos < end) {
detail::moveToUninitialized(begin + pos, end, out + pos + 1);
}
} catch (...) {
for (Size i = 0; i <= pos; ++i) {
out[i].~T();
}
throw;
}
}
/* /*
* Move objects in memory to the right into some uninitialized * Move objects in memory to the right into some uninitialized
* memory, where the region overlaps. This doesn't just use * memory, where the region overlaps. This doesn't just use
...@@ -223,10 +157,10 @@ void populateMemForward(T* mem, std::size_t n, Function const& op) { ...@@ -223,10 +157,10 @@ void populateMemForward(T* mem, std::size_t n, Function const& op) {
} }
template <class SizeType, bool ShouldUseHeap> template <class SizeType, bool ShouldUseHeap>
struct IntegralSizePolicy { struct IntegralSizePolicyBase {
typedef SizeType InternalSizeType; typedef SizeType InternalSizeType;
IntegralSizePolicy() : size_(0) {} IntegralSizePolicyBase() : size_(0) {}
protected: protected:
static constexpr std::size_t policyMaxSize() { static constexpr std::size_t policyMaxSize() {
...@@ -254,7 +188,7 @@ struct IntegralSizePolicy { ...@@ -254,7 +188,7 @@ struct IntegralSizePolicy {
size_ = (kExternMask & size_) | SizeType(sz); size_ = (kExternMask & size_) | SizeType(sz);
} }
void swapSizePolicy(IntegralSizePolicy& o) { void swapSizePolicy(IntegralSizePolicyBase& o) {
std::swap(size_, o.size_); std::swap(size_, o.size_);
} }
...@@ -268,6 +202,101 @@ struct IntegralSizePolicy { ...@@ -268,6 +202,101 @@ struct IntegralSizePolicy {
SizeType size_; SizeType size_;
}; };
template <class SizeType, bool ShouldUseHeap>
struct IntegralSizePolicy;
template <class SizeType>
struct IntegralSizePolicy<SizeType, true>
: public IntegralSizePolicyBase<SizeType, true> {
public:
/*
* Move a range to a range of uninitialized memory. Assumes the
* ranges don't overlap.
*/
template <class T>
typename std::enable_if<!FOLLY_IS_TRIVIALLY_COPYABLE(T)>::type
moveToUninitialized(T* first, T* last, T* out) {
std::size_t idx = 0;
try {
for (; first != last; ++first, ++idx) {
new (&out[idx]) T(std::move(*first));
}
} catch (...) {
// Even for callers trying to give the strong guarantee
// (e.g. push_back) it's ok to assume here that we don't have to
// move things back and that it was a copy constructor that
// threw: if someone throws from a move constructor the effects
// are unspecified.
for (std::size_t i = 0; i < idx; ++i) {
out[i].~T();
}
throw;
}
}
// Specialization for trivially copyable types.
template <class T>
typename std::enable_if<FOLLY_IS_TRIVIALLY_COPYABLE(T)>::type
moveToUninitialized(T* first, T* last, T* out) {
std::memmove(out, first, (last - first) * sizeof *first);
}
/*
* Move a range to a range of uninitialized memory. Assumes the
* ranges don't overlap. Inserts an element at out + pos using
* emplaceFunc(). out will contain (end - begin) + 1 elements on success and
* none on failure. If emplaceFunc() throws [begin, end) is unmodified.
*/
template <class T, class EmplaceFunc>
void moveToUninitializedEmplace(
T* begin,
T* end,
T* out,
SizeType pos,
EmplaceFunc&& emplaceFunc) {
// Must be called first so that if it throws [begin, end) is unmodified.
// We have to support the strong exception guarantee for emplace_back().
emplaceFunc(out + pos);
// move old elements to the left of the new one
try {
this->moveToUninitialized(begin, begin + pos, out);
} catch (...) {
out[pos].~T();
throw;
}
// move old elements to the right of the new one
try {
if (begin + pos < end) {
this->moveToUninitialized(begin + pos, end, out + pos + 1);
}
} catch (...) {
for (SizeType i = 0; i <= pos; ++i) {
out[i].~T();
}
throw;
}
}
};
template <class SizeType>
struct IntegralSizePolicy<SizeType, false>
: public IntegralSizePolicyBase<SizeType, false> {
public:
template <class T>
void moveToUninitialized(T* /*first*/, T* /*last*/, T* /*out*/) {
assume_unreachable();
}
template <class T, class EmplaceFunc>
void moveToUninitializedEmplace(
T* /* begin */,
T* /* end */,
T* /* out */,
SizeType /* pos */,
EmplaceFunc&& /* emplaceFunc */) {
assume_unreachable();
}
};
/* /*
* If you're just trying to use this class, ignore everything about * If you're just trying to use this class, ignore everything about
* this next small_vector_base class thing. * this next small_vector_base class thing.
...@@ -977,6 +1006,15 @@ class small_vector : public detail::small_vector_base< ...@@ -977,6 +1006,15 @@ class small_vector : public detail::small_vector_base<
assert(!insert); assert(!insert);
return; return;
} }
assert(this->kShouldUseHeap);
// This branch isn't needed for correctness, but allows the optimizer to
// skip generating code for the rest of this function in NoHeap
// small_vectors.
if (!this->kShouldUseHeap) {
return;
}
newSize = std::max(newSize, computeNewSize()); newSize = std::max(newSize, computeNewSize());
auto needBytes = newSize * sizeof(value_type); auto needBytes = newSize * sizeof(value_type);
...@@ -1001,11 +1039,11 @@ class small_vector : public detail::small_vector_base< ...@@ -1001,11 +1039,11 @@ class small_vector : public detail::small_vector_base<
try { try {
if (insert) { if (insert) {
// move and insert the new element // move and insert the new element
detail::moveToUninitializedEmplace( this->moveToUninitializedEmplace(
begin(), end(), newp, pos, std::forward<EmplaceFunc>(emplaceFunc)); begin(), end(), newp, pos, std::forward<EmplaceFunc>(emplaceFunc));
} else { } else {
// move without inserting new element // move without inserting new element
detail::moveToUninitialized(begin(), end(), newp); this->moveToUninitialized(begin(), end(), newp);
} }
} catch (...) { } catch (...) {
free(newh); free(newh);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment