Commit 66270d4e authored by Mark Santaniello's avatar Mark Santaniello Committed by Facebook Github Bot

more pmr support for sorted_vector_types

Summary:
- Add allocator-extended copy constructors
- Add allocator-extended move constructors
- Add allocator-extended initializer-list constructor
- Add get_allocator accessor
- Make emplace/emplace_hint use the allocator
- pmr tests catch allocations that "escape" by setting the default resource to null

Reviewed By: nbronson

Differential Revision: D16623448

fbshipit-source-id: c10682f98d1c234fcad95608643b2f3ccea64a41
parent f9b0a56b
...@@ -67,11 +67,13 @@ ...@@ -67,11 +67,13 @@
#include <cassert> #include <cassert>
#include <initializer_list> #include <initializer_list>
#include <iterator> #include <iterator>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <folly/ScopeGuard.h>
#include <folly/Traits.h> #include <folly/Traits.h>
#include <folly/Utility.h> #include <folly/Utility.h>
#include <folly/lang/Exception.h> #include <folly/lang/Exception.h>
...@@ -259,6 +261,8 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -259,6 +261,8 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
using if_is_transparent = using if_is_transparent =
_t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>; _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>;
struct EBO;
public: public:
typedef T value_type; typedef T value_type;
typedef T key_type; typedef T key_type;
...@@ -284,6 +288,17 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -284,6 +288,17 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
sorted_vector_set() : m_(Compare(), Allocator()) {} sorted_vector_set() : m_(Compare(), Allocator()) {}
sorted_vector_set(const sorted_vector_set&) = default;
sorted_vector_set(const sorted_vector_set& other, const Allocator& alloc)
: m_(other.m_, alloc) {}
sorted_vector_set(sorted_vector_set&&) = default;
sorted_vector_set(sorted_vector_set&& other, const Allocator& alloc) noexcept(
std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value)
: m_(std::move(other.m_), alloc) {}
explicit sorted_vector_set(const Allocator& alloc) : m_(Compare(), alloc) {} explicit sorted_vector_set(const Allocator& alloc) : m_(Compare(), alloc) {}
explicit sorted_vector_set( explicit sorted_vector_set(
...@@ -292,7 +307,7 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -292,7 +307,7 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
: m_(comp, alloc) {} : m_(comp, alloc) {}
template <class InputIterator> template <class InputIterator>
explicit sorted_vector_set( sorted_vector_set(
InputIterator first, InputIterator first,
InputIterator last, InputIterator last,
const Compare& comp = Compare(), const Compare& comp = Compare(),
...@@ -303,6 +318,17 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -303,6 +318,17 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
insert(first, last); insert(first, last);
} }
template <class InputIterator>
sorted_vector_set(
InputIterator first,
InputIterator last,
const Allocator& alloc)
: m_(Compare(), alloc) {
// This is linear if [first, last) is already sorted (and if we
// can figure out the distance between the two iterators).
insert(first, last);
}
/* implicit */ sorted_vector_set( /* implicit */ sorted_vector_set(
std::initializer_list<value_type> list, std::initializer_list<value_type> list,
const Compare& comp = Compare(), const Compare& comp = Compare(),
...@@ -311,6 +337,13 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -311,6 +337,13 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
insert(list.begin(), list.end()); insert(list.begin(), list.end());
} }
sorted_vector_set(
std::initializer_list<value_type> list,
const Allocator& alloc)
: m_(Compare(), alloc) {
insert(list.begin(), list.end());
}
// Construct a sorted_vector_set by stealing the storage of a prefilled // Construct a sorted_vector_set by stealing the storage of a prefilled
// container. The container need not be sorted already. This supports // container. The container need not be sorted already. This supports
// bulk construction of sorted_vector_set with zero allocations, not counting // bulk construction of sorted_vector_set with zero allocations, not counting
...@@ -339,12 +372,23 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -339,12 +372,23 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
sorted_vector_set( sorted_vector_set(
sorted_unique_t, sorted_unique_t,
Container&& container, Container&& container,
const Compare& comp = Compare()) const Compare& comp = Compare()) noexcept(std::
: m_(comp, container.get_allocator()) { is_nothrow_constructible<
assert(detail::is_sorted_unique(container, value_comp())); EBO,
m_.cont_.swap(container); const Compare&,
Container&&>::value)
: m_(comp, std::move(container)) {
assert(detail::is_sorted_unique(m_.cont_, value_comp()));
} }
Allocator get_allocator() const {
return m_.cont_.get_allocator();
}
sorted_vector_set& operator=(const sorted_vector_set& other) = default;
sorted_vector_set& operator=(sorted_vector_set&& other) = default;
sorted_vector_set& operator=(std::initializer_list<value_type> ilist) { sorted_vector_set& operator=(std::initializer_list<value_type> ilist) {
clear(); clear();
insert(ilist.begin(), ilist.end()); insert(ilist.begin(), ilist.end());
...@@ -452,8 +496,14 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -452,8 +496,14 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
// compatibility // compatibility
template <typename... Args> template <typename... Args>
std::pair<iterator, bool> emplace(Args&&... args) { std::pair<iterator, bool> emplace(Args&&... args) {
value_type v(std::forward<Args>(args)...); std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b;
return insert(std::move(v)); value_type* p = static_cast<value_type*>(static_cast<void*>(&b));
auto a = get_allocator();
std::allocator_traits<allocator_type>::construct(
a, p, std::forward<Args>(args)...);
auto g = makeGuard(
[&]() { std::allocator_traits<allocator_type>::destroy(a, p); });
return insert(std::move(*p));
} }
std::pair<iterator, bool> emplace(const value_type& value) { std::pair<iterator, bool> emplace(const value_type& value) {
...@@ -468,8 +518,14 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -468,8 +518,14 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
// compatibility // compatibility
template <typename... Args> template <typename... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) { iterator emplace_hint(const_iterator hint, Args&&... args) {
value_type v(std::forward<Args>(args)...); std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b;
return insert(hint, std::move(v)); value_type* p = static_cast<value_type*>(static_cast<void*>(&b));
auto a = get_allocator();
std::allocator_traits<allocator_type>::construct(
a, p, std::forward<Args>(args)...);
auto g = makeGuard(
[&]() { std::allocator_traits<allocator_type>::destroy(a, p); });
return insert(hint, std::move(*p));
} }
iterator emplace_hint(const_iterator hint, const value_type& value) { iterator emplace_hint(const_iterator hint, const value_type& value) {
...@@ -627,8 +683,18 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -627,8 +683,18 @@ class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {
* More info: http://www.cantrip.org/emptyopt.html * More info: http://www.cantrip.org/emptyopt.html
*/ */
struct EBO : Compare { struct EBO : Compare {
explicit EBO(const Compare& c, const Allocator& alloc) explicit EBO(const Compare& c, const Allocator& alloc) noexcept(
std::is_nothrow_default_constructible<Container>::value)
: Compare(c), cont_(alloc) {} : Compare(c), cont_(alloc) {}
EBO(EBO&& other, const Allocator& alloc)
noexcept(std::is_nothrow_constructible<
Container,
Container&&,
const Allocator&>::value)
: Compare(), cont_(std::move(other.cont_), alloc) {}
EBO(const Compare& c, Container&& cont)
noexcept(std::is_nothrow_move_constructible<Container>::value)
: Compare(c), cont_(std::move(cont)) {}
Container cont_; Container cont_;
} m_; } m_;
...@@ -709,6 +775,8 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -709,6 +775,8 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
using if_is_transparent = using if_is_transparent =
_t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>; _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>;
struct EBO;
public: public:
typedef Key key_type; typedef Key key_type;
typedef Value mapped_type; typedef Value mapped_type;
...@@ -737,7 +805,20 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -737,7 +805,20 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
typedef typename Container::reverse_iterator reverse_iterator; typedef typename Container::reverse_iterator reverse_iterator;
typedef typename Container::const_reverse_iterator const_reverse_iterator; typedef typename Container::const_reverse_iterator const_reverse_iterator;
sorted_vector_map() : m_(value_compare(Compare()), Allocator()) {} sorted_vector_map() noexcept(
std::is_nothrow_constructible<EBO, value_compare, Allocator>::value)
: m_(value_compare(Compare()), Allocator()) {}
sorted_vector_map(const sorted_vector_map&) = default;
sorted_vector_map(const sorted_vector_map& other, const Allocator& alloc)
: m_(other.m_, alloc) {}
sorted_vector_map(sorted_vector_map&&) = default;
sorted_vector_map(sorted_vector_map&& other, const Allocator& alloc) noexcept(
std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value)
: m_(std::move(other.m_), alloc) {}
explicit sorted_vector_map(const Allocator& alloc) explicit sorted_vector_map(const Allocator& alloc)
: m_(value_compare(Compare()), alloc) {} : m_(value_compare(Compare()), alloc) {}
...@@ -757,7 +838,16 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -757,7 +838,16 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
insert(first, last); insert(first, last);
} }
explicit sorted_vector_map( template <class InputIterator>
sorted_vector_map(
InputIterator first,
InputIterator last,
const Allocator& alloc)
: m_(value_compare(Compare()), alloc) {
insert(first, last);
}
/* implicit */ sorted_vector_map(
std::initializer_list<value_type> list, std::initializer_list<value_type> list,
const Compare& comp = Compare(), const Compare& comp = Compare(),
const Allocator& alloc = Allocator()) const Allocator& alloc = Allocator())
...@@ -765,6 +855,13 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -765,6 +855,13 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
insert(list.begin(), list.end()); insert(list.begin(), list.end());
} }
sorted_vector_map(
std::initializer_list<value_type> list,
const Allocator& alloc)
: m_(value_compare(Compare()), alloc) {
insert(list.begin(), list.end());
}
// Construct a sorted_vector_map by stealing the storage of a prefilled // Construct a sorted_vector_map by stealing the storage of a prefilled
// container. The container need not be sorted already. This supports // container. The container need not be sorted already. This supports
// bulk construction of sorted_vector_map with zero allocations, not counting // bulk construction of sorted_vector_map with zero allocations, not counting
...@@ -793,13 +890,24 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -793,13 +890,24 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
sorted_vector_map( sorted_vector_map(
sorted_unique_t, sorted_unique_t,
Container&& container, Container&& container,
const Compare& comp = Compare()) const Compare& comp = Compare()) noexcept(std::
: m_(value_compare(comp), container.get_allocator()) { is_nothrow_constructible<
assert(std::is_sorted(container.begin(), container.end(), value_comp())); EBO,
assert(detail::is_sorted_unique(container, value_comp())); value_compare,
m_.cont_.swap(container); Container&&>::value)
: m_(value_compare(comp), std::move(container)) {
assert(std::is_sorted(m_.cont_.begin(), m_.cont_.end(), value_comp()));
assert(detail::is_sorted_unique(m_.cont_, value_comp()));
}
Allocator get_allocator() const {
return m_.cont_.get_allocator();
} }
sorted_vector_map& operator=(const sorted_vector_map& other) = default;
sorted_vector_map& operator=(sorted_vector_map&& other) = default;
sorted_vector_map& operator=(std::initializer_list<value_type> ilist) { sorted_vector_map& operator=(std::initializer_list<value_type> ilist) {
clear(); clear();
insert(ilist.begin(), ilist.end()); insert(ilist.begin(), ilist.end());
...@@ -907,8 +1015,14 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -907,8 +1015,14 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
// compatibility // compatibility
template <typename... Args> template <typename... Args>
std::pair<iterator, bool> emplace(Args&&... args) { std::pair<iterator, bool> emplace(Args&&... args) {
value_type v(std::forward<Args>(args)...); std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b;
return insert(std::move(v)); value_type* p = static_cast<value_type*>(static_cast<void*>(&b));
auto a = get_allocator();
std::allocator_traits<allocator_type>::construct(
a, p, std::forward<Args>(args)...);
auto g = makeGuard(
[&]() { std::allocator_traits<allocator_type>::destroy(a, p); });
return insert(std::move(*p));
} }
std::pair<iterator, bool> emplace(const value_type& value) { std::pair<iterator, bool> emplace(const value_type& value) {
...@@ -923,8 +1037,14 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -923,8 +1037,14 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
// compatibility // compatibility
template <typename... Args> template <typename... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) { iterator emplace_hint(const_iterator hint, Args&&... args) {
value_type v(std::forward<Args>(args)...); std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b;
return insert(hint, std::move(v)); value_type* p = static_cast<value_type*>(static_cast<void*>(&b));
auto a = get_allocator();
std::allocator_traits<allocator_type>::construct(
a, p, std::forward<Args>(args)...);
auto g = makeGuard(
[&]() { std::allocator_traits<allocator_type>::destroy(a, p); });
return insert(hint, std::move(*p));
} }
iterator emplace_hint(const_iterator hint, const value_type& value) { iterator emplace_hint(const_iterator hint, const value_type& value) {
...@@ -1097,8 +1217,18 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { ...@@ -1097,8 +1217,18 @@ class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {
// This is to get the empty base optimization; see the comment in // This is to get the empty base optimization; see the comment in
// sorted_vector_set. // sorted_vector_set.
struct EBO : value_compare { struct EBO : value_compare {
explicit EBO(const value_compare& c, const Allocator& alloc) explicit EBO(const value_compare& c, const Allocator& alloc) noexcept(
std::is_nothrow_default_constructible<Container>::value)
: value_compare(c), cont_(alloc) {} : value_compare(c), cont_(alloc) {}
EBO(EBO&& other, const Allocator& alloc)
noexcept(std::is_nothrow_constructible<
Container,
Container&&,
const Allocator&>::value)
: value_compare(Compare()), cont_(std::move(other.cont_), alloc) {}
EBO(const Compare& c, Container&& cont)
noexcept(std::is_nothrow_move_constructible<Container>::value)
: value_compare(c), cont_(std::move(cont)) {}
Container cont_; Container cont_;
} m_; } m_;
......
...@@ -856,29 +856,172 @@ TEST(SortedVectorTypes, TestEmplaceHint) { ...@@ -856,29 +856,172 @@ TEST(SortedVectorTypes, TestEmplaceHint) {
#if FOLLY_HAS_MEMORY_RESOURCE #if FOLLY_HAS_MEMORY_RESOURCE
using folly::detail::std_pmr::new_delete_resource;
using folly::detail::std_pmr::null_memory_resource;
using folly::detail::std_pmr::polymorphic_allocator;
using folly::detail::std_pmr::resource_adaptor;
namespace {
struct test_resource : public resource_adaptor<std::allocator<char>> {
bool do_is_equal(const memory_resource& other) const noexcept override {
return this == &other;
}
};
} // namespace
TEST(SortedVectorTypes, TestPmrAllocatorSimple) { TEST(SortedVectorTypes, TestPmrAllocatorSimple) {
folly::pmr::sorted_vector_set<std::pair<int, int>> set{ namespace pmr = folly::pmr;
folly::detail::std_pmr::null_memory_resource()};
EXPECT_THROW(set.emplace(42, 42), std::bad_alloc); pmr::sorted_vector_set<std::pair<int, int>> s(null_memory_resource());
EXPECT_THROW(s.emplace(42, 42), std::bad_alloc);
folly::pmr::sorted_vector_map<int, int> map{ pmr::sorted_vector_map<int, int> m(null_memory_resource());
folly::detail::std_pmr::null_memory_resource()}; EXPECT_THROW(m.emplace(42, 42), std::bad_alloc);
EXPECT_THROW(map.emplace(42, 42), std::bad_alloc); }
TEST(SortedVectorTypes, TestPmrMoveConstructSameAlloc) {
namespace pmr = folly::pmr;
set_default_resource(null_memory_resource());
test_resource r;
polymorphic_allocator<std::byte> a1(&r), a2(&r);
EXPECT_EQ(a1, a2);
{
pmr::sorted_vector_set<int> s1(a1);
s1.emplace(42);
auto d = s1.data();
pmr::sorted_vector_set<int> s2(std::move(s1), a2);
EXPECT_EQ(s2.data(), d);
}
{
pmr::sorted_vector_map<int, int> m1(a1);
m1.emplace(42, 42);
auto d = m1.data();
pmr::sorted_vector_map<int, int> m2(std::move(m1), a2);
EXPECT_EQ(m2.data(), d);
}
}
TEST(SortedVectorTypes, TestPmrMoveConstructDifferentAlloc) {
namespace pmr = folly::pmr;
set_default_resource(null_memory_resource());
test_resource r1, r2;
polymorphic_allocator<std::byte> a1(&r1), a2(&r2);
EXPECT_NE(a1, a2);
{
pmr::sorted_vector_set<int> s1(a1);
s1.emplace(42);
auto d = s1.data();
pmr::sorted_vector_set<int> s2(std::move(s1), a2);
EXPECT_NE(s2.data(), d);
}
{
pmr::sorted_vector_map<int, int> m1(a1);
m1.emplace(42, 42);
auto d = m1.data();
pmr::sorted_vector_map<int, int> m2(std::move(m1), a2);
EXPECT_NE(m2.data(), d);
}
} }
template <typename T>
using pmr_vector =
std::vector<T, folly::detail::std_pmr::polymorphic_allocator<T>>;
TEST(SortedVectorTypes, TestPmrAllocatorScoped) { TEST(SortedVectorTypes, TestPmrAllocatorScoped) {
using AllocT = folly::detail::std_pmr::polymorphic_allocator<char>; namespace pmr = folly::pmr;
using VectorT = std::vector<int, AllocT>;
set_default_resource(null_memory_resource());
polymorphic_allocator<std::byte> alloc(new_delete_resource());
{
pmr::sorted_vector_set<pmr_vector<int>> s(alloc);
s.emplace(1);
EXPECT_EQ(s.begin()->get_allocator(), alloc);
}
{
pmr::sorted_vector_set<pmr_vector<int>> s(alloc);
s.emplace_hint(s.begin(), 1);
EXPECT_EQ(s.begin()->get_allocator(), alloc);
}
{
pmr::sorted_vector_map<int, pmr_vector<int>> m(alloc);
m.emplace(1, 1);
EXPECT_EQ(m.begin()->second.get_allocator(), alloc);
}
{
pmr::sorted_vector_map<int, pmr_vector<int>> m(alloc);
m.emplace_hint(m.begin(), 1, 1);
EXPECT_EQ(m.begin()->second.get_allocator(), alloc);
}
{
pmr::sorted_vector_set<pmr::sorted_vector_map<int, int>> s(alloc);
s.emplace(std::initializer_list<std::pair<int, int>>{{42, 42}});
EXPECT_EQ(s.begin()->get_allocator(), alloc);
}
{
pmr::sorted_vector_set<pmr::sorted_vector_map<int, int>> s(alloc);
s.emplace_hint(
s.begin(), std::initializer_list<std::pair<int, int>>{{42, 42}});
EXPECT_EQ(s.begin()->get_allocator(), alloc);
}
{
pmr::sorted_vector_set<pmr::sorted_vector_set<int>> s(alloc);
s.emplace(std::initializer_list<int>{1});
EXPECT_EQ(s.begin()->get_allocator(), alloc);
}
{
pmr::sorted_vector_set<pmr::sorted_vector_set<int>> s(alloc);
s.emplace_hint(s.begin(), std::initializer_list<int>{1});
EXPECT_EQ(s.begin()->get_allocator(), alloc);
}
AllocT alloc; {
pmr::sorted_vector_map<int, pmr::sorted_vector_map<int, int>> m(alloc);
m.emplace(
std::piecewise_construct,
std::forward_as_tuple(42),
std::forward_as_tuple(
std::initializer_list<std::pair<int, int>>{{42, 42}}));
EXPECT_EQ(m.begin()->second.get_allocator(), alloc);
}
folly::pmr::sorted_vector_set<VectorT> set{alloc}; {
set.emplace(42); pmr::sorted_vector_map<int, pmr::sorted_vector_map<int, int>> m(alloc);
EXPECT_EQ(set.begin()->get_allocator(), alloc); m.emplace_hint(
m.begin(),
std::piecewise_construct,
std::forward_as_tuple(42),
std::forward_as_tuple(
std::initializer_list<std::pair<int, int>>{{42, 42}}));
EXPECT_EQ(m.begin()->second.get_allocator(), alloc);
}
folly::pmr::sorted_vector_map<int, VectorT> map{alloc}; {
map.emplace(42, 42); pmr::sorted_vector_map<int, pmr::sorted_vector_map<int, int>> m(alloc);
EXPECT_EQ(map.begin()->second.get_allocator(), alloc); m[42][42] = 42;
EXPECT_EQ(m.begin()->second.get_allocator(), alloc);
}
} }
#endif #endif
......
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