Commit 891c8860 authored by Huseyin Tan's avatar Huseyin Tan Committed by Facebook GitHub Bot

Fix allocate_unique for cases where allocator with different type is provided

Summary:
While working on adding allocator support for thrift reference data types (cpp.ref) I realized that allocate_unique always expects the allocator to be of the same type. This means we cannot allocate a unique_ptr<T> with a Alloc<char>. Other allocate methods like std::allocate_shared allows this and it is quite useful. This is critical since it is almost impossible to pass a scoped_allocator of a different type for all the containers in a struct if this kind of allocator type conversion magic is not supported.

This diff adds support to folly::allocate_unique to use an allocator for different type.

Reviewed By: yfeldblum

Differential Revision: D33026177

fbshipit-source-id: 88c43374c2482b76697485c1667064441a057117
parent 798c93a2
......@@ -752,12 +752,18 @@ class allocator_delete : private std::remove_reference<Alloc>::type {
* allocate_unique, like std::allocate_shared but for std::unique_ptr
*/
template <typename T, typename Alloc, typename... Args>
std::unique_ptr<T, allocator_delete<Alloc>> allocate_unique(
Alloc const& alloc, Args&&... args) {
using traits = std::allocator_traits<Alloc>;
std::unique_ptr<
T,
allocator_delete<
typename std::allocator_traits<Alloc>::template rebind_alloc<T>>>
allocate_unique(Alloc const& alloc, Args&&... args) {
using TAlloc =
typename std::allocator_traits<Alloc>::template rebind_alloc<T>;
using traits = std::allocator_traits<TAlloc>;
struct DeferCondDeallocate {
bool& cond;
Alloc& copy;
TAlloc& copy;
T* p;
~DeferCondDeallocate() {
if (FOLLY_UNLIKELY(!cond)) {
......@@ -765,7 +771,7 @@ std::unique_ptr<T, allocator_delete<Alloc>> allocate_unique(
}
}
};
auto copy = alloc;
auto copy = TAlloc(alloc);
auto const p = traits::allocate(copy, 1);
{
bool constructed = false;
......@@ -773,7 +779,7 @@ std::unique_ptr<T, allocator_delete<Alloc>> allocate_unique(
traits::construct(copy, p, static_cast<Args&&>(args)...);
constructed = true;
}
return {p, allocator_delete<Alloc>(std::move(copy))};
return {p, allocator_delete<TAlloc>(std::move(copy))};
}
struct SysBufferDeleter {
......
......@@ -213,6 +213,14 @@ TEST(SysAllocator, allocate_unique) {
EXPECT_EQ(3., *ptr);
}
TEST(SysAllocator, allocate_unique_different_type) {
using Alloc = SysAllocator<char>;
Alloc const alloc;
std::unique_ptr<float, allocator_delete<SysAllocator<float>>> ptr =
allocate_unique<float>(alloc, 3.);
EXPECT_EQ(3., *ptr);
}
TEST(SysAllocator, vector) {
using Alloc = SysAllocator<float>;
Alloc const alloc;
......@@ -241,7 +249,8 @@ TEST(AlignedSysAllocator, equality_fixed) {
TEST(AlignedSysAllocator, allocate_unique_fixed) {
using Alloc = AlignedSysAllocator<float, FixedAlign<1024>>;
Alloc const alloc;
auto ptr = allocate_unique<float>(alloc, 3.);
std::unique_ptr<float, allocator_delete<Alloc>> ptr =
allocate_unique<float>(alloc, 3.);
EXPECT_EQ(3., *ptr);
EXPECT_EQ(0, std::uintptr_t(ptr.get()) % 1024);
}
......@@ -342,19 +351,40 @@ struct CountedAllocatorStats {
};
template <typename T>
class CountedAllocator : public std::allocator<T> {
class CountedAllocator {
private:
CountedAllocatorStats* stats_;
std::allocator<T> alloc;
public:
using value_type = T;
constexpr CountedAllocator(CountedAllocator const&) = default;
CountedAllocator<T>& operator=(CountedAllocator const&) = default;
template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>
/* implicit */ constexpr CountedAllocator(
CountedAllocator<U> const& other) noexcept
: stats_(other.stats_) {}
explicit CountedAllocator(CountedAllocatorStats& stats) noexcept
: stats_(&stats) {}
T* allocate(size_t count) { return alloc.allocate(count); }
void deallocate(T* p, size_t n) {
std::allocator<T>::deallocate(p, n);
alloc.deallocate(p, n);
++stats_->deallocates;
}
};
template <class T, class U>
bool operator==(const CountedAllocator<T>&, const CountedAllocator<U>&) {
return true;
}
template <class T, class U>
bool operator!=(const CountedAllocator<T>&, const CountedAllocator<U>&) {
return false;
}
TEST(allocate_unique, ctor_failure) {
struct CtorThrows {
explicit CtorThrows(bool cond) {
......
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