Respect std::uses_allocator trait in utils::Allocator
Reviewers: mtomic, llugovic, mferencevic Reviewed By: llugovic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2081
This commit is contained in:
parent
64a05bc972
commit
18cf877a47
@ -5,6 +5,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
// Although <memory_resource> is in C++17, gcc libstdc++ still needs to
|
// Although <memory_resource> is in C++17, gcc libstdc++ still needs to
|
||||||
// implement it fully. It should be available in the next major release
|
// implement it fully. It should be available in the next major release
|
||||||
@ -76,6 +77,11 @@ MemoryResource *NewDeleteResource() noexcept;
|
|||||||
/// The API of the Allocator is made such that it fits with the C++ STL
|
/// The API of the Allocator is made such that it fits with the C++ STL
|
||||||
/// requirements. It can be freely used in STL containers while relying on our
|
/// requirements. It can be freely used in STL containers while relying on our
|
||||||
/// implementations of MemoryResource.
|
/// implementations of MemoryResource.
|
||||||
|
///
|
||||||
|
/// Classes which have a member allocator_type typedef to this allocator will
|
||||||
|
/// receive an instance of `this->GetMemoryResource()` upon construction if the
|
||||||
|
/// outer container also uses this allocator. For concrete behaviour on how this
|
||||||
|
/// is done refer to `std::uses_allocator` in C++ reference.
|
||||||
template <class T>
|
template <class T>
|
||||||
class Allocator {
|
class Allocator {
|
||||||
public:
|
public:
|
||||||
@ -100,6 +106,27 @@ class Allocator {
|
|||||||
memory_->Allocate(count_elements * sizeof(T), alignof(T)));
|
memory_->Allocate(count_elements * sizeof(T), alignof(T)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class U, class... TArgs>
|
||||||
|
void construct(U *ptr, TArgs &&... args) {
|
||||||
|
if constexpr (std::uses_allocator_v<U, Allocator>) {
|
||||||
|
if constexpr (std::is_constructible_v<U, std::allocator_arg_t,
|
||||||
|
MemoryResource *, TArgs...>) {
|
||||||
|
::new (ptr)
|
||||||
|
U(std::allocator_arg, memory_, std::forward<TArgs>(args)...);
|
||||||
|
} else if constexpr (std::is_constructible_v<U, TArgs...,
|
||||||
|
MemoryResource *>) {
|
||||||
|
::new (ptr) U(std::forward<TArgs>(args)..., memory_);
|
||||||
|
} else {
|
||||||
|
static_assert(!std::uses_allocator_v<U, Allocator>,
|
||||||
|
"Class declares std::uses_allocator but has no valid "
|
||||||
|
"constructor overload. Refer to 'Uses-allocator "
|
||||||
|
"construction' rules in C++ reference.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
::new (ptr) U(std::forward<TArgs>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void deallocate(T *p, size_t count_elements) {
|
void deallocate(T *p, size_t count_elements) {
|
||||||
memory_->Deallocate(p, count_elements * sizeof(T), alignof(T));
|
memory_->Deallocate(p, count_elements * sizeof(T), alignof(T));
|
||||||
}
|
}
|
||||||
|
@ -146,3 +146,62 @@ TEST(MonotonicBufferResource, AllocationWithSizeOverflow) {
|
|||||||
mem.Allocate(1, 1);
|
mem.Allocate(1, 1);
|
||||||
EXPECT_THROW(mem.Allocate(max_size, 4), std::bad_alloc);
|
EXPECT_THROW(mem.Allocate(max_size, 4), std::bad_alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
|
class ContainerWithAllocatorLast final {
|
||||||
|
public:
|
||||||
|
using allocator_type = utils::Allocator<int>;
|
||||||
|
|
||||||
|
ContainerWithAllocatorLast() = default;
|
||||||
|
explicit ContainerWithAllocatorLast(int value) : value_(value) {}
|
||||||
|
ContainerWithAllocatorLast(int value, utils::MemoryResource *memory)
|
||||||
|
: memory_(memory), value_(value) {}
|
||||||
|
|
||||||
|
ContainerWithAllocatorLast(const ContainerWithAllocatorLast &other)
|
||||||
|
: value_(other.value_) {}
|
||||||
|
ContainerWithAllocatorLast(const ContainerWithAllocatorLast &other,
|
||||||
|
utils::MemoryResource *memory)
|
||||||
|
: memory_(memory), value_(other.value_) {}
|
||||||
|
|
||||||
|
utils::MemoryResource *memory_{nullptr};
|
||||||
|
int value_{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
|
class ContainerWithAllocatorFirst final {
|
||||||
|
public:
|
||||||
|
using allocator_type = utils::Allocator<int>;
|
||||||
|
|
||||||
|
ContainerWithAllocatorFirst() = default;
|
||||||
|
explicit ContainerWithAllocatorFirst(int value) : value_(value) {}
|
||||||
|
ContainerWithAllocatorFirst(std::allocator_arg_t,
|
||||||
|
utils::MemoryResource *memory, int value)
|
||||||
|
: memory_(memory), value_(value) {}
|
||||||
|
|
||||||
|
ContainerWithAllocatorFirst(const ContainerWithAllocatorFirst &other)
|
||||||
|
: value_(other.value_) {}
|
||||||
|
ContainerWithAllocatorFirst(std::allocator_arg_t,
|
||||||
|
utils::MemoryResource *memory,
|
||||||
|
const ContainerWithAllocatorFirst &other)
|
||||||
|
: memory_(memory), value_(other.value_) {}
|
||||||
|
|
||||||
|
utils::MemoryResource *memory_{nullptr};
|
||||||
|
int value_{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class AllocatorTest : public ::testing::Test {};
|
||||||
|
|
||||||
|
using ContainersWithAllocators =
|
||||||
|
::testing::Types<ContainerWithAllocatorLast, ContainerWithAllocatorFirst>;
|
||||||
|
|
||||||
|
TYPED_TEST_CASE(AllocatorTest, ContainersWithAllocators);
|
||||||
|
|
||||||
|
TYPED_TEST(AllocatorTest, PropagatesToStdUsesAllocator) {
|
||||||
|
std::vector<TypeParam, utils::Allocator<TypeParam>> vec(
|
||||||
|
utils::NewDeleteResource());
|
||||||
|
vec.emplace_back(42);
|
||||||
|
const auto &c = vec.front();
|
||||||
|
EXPECT_EQ(c.value_, 42);
|
||||||
|
EXPECT_EQ(c.memory_, utils::NewDeleteResource());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user