189 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9 10// <memory>11 12// To allow checking that self-move works correctly.13// ADDITIONAL_COMPILE_FLAGS: -Wno-self-move14 15// template<class _Alloc>16// struct __allocation_guard;17 18#include <__cxx03/__memory/allocation_guard.h>19#include <cassert>20#include <type_traits>21#include <utility>22 23#include "test_allocator.h"24 25using A = test_allocator<int>;26 27// A trimmed-down version of `test_allocator` that is copy-assignable (in general allocators don't have to support copy28// assignment).29template <class T>30struct AssignableAllocator {31 using size_type = unsigned;32 using difference_type = int;33 using value_type = T;34 using pointer = value_type*;35 using const_pointer = const value_type*;36 using reference = typename std::add_lvalue_reference<value_type>::type;37 using const_reference = typename std::add_lvalue_reference<const value_type>::type;38 39 template <class U>40 struct rebind {41 using other = test_allocator<U>;42 };43 44 test_allocator_statistics* stats_ = nullptr;45 46 explicit AssignableAllocator(test_allocator_statistics& stats) : stats_(&stats) {47 ++stats_->count;48 }49 50 TEST_CONSTEXPR_CXX14 AssignableAllocator(const AssignableAllocator& rhs) TEST_NOEXCEPT51 : stats_(rhs.stats_) {52 if (stats_ != nullptr) {53 ++stats_->count;54 ++stats_->copied;55 }56 }57 58 TEST_CONSTEXPR_CXX14 AssignableAllocator& operator=(const AssignableAllocator& rhs) TEST_NOEXCEPT {59 stats_ = rhs.stats_;60 if (stats_ != nullptr) {61 ++stats_->count;62 ++stats_->copied;63 }64 65 return *this;66 }67 68 TEST_CONSTEXPR_CXX14 pointer allocate(size_type n, const void* = nullptr) {69 if (stats_ != nullptr) {70 ++stats_->alloc_count;71 }72 return std::allocator<value_type>().allocate(n);73 }74 75 TEST_CONSTEXPR_CXX14 void deallocate(pointer p, size_type s) {76 if (stats_ != nullptr) {77 --stats_->alloc_count;78 }79 std::allocator<value_type>().deallocate(p, s);80 }81 82 TEST_CONSTEXPR size_type max_size() const TEST_NOEXCEPT { return UINT_MAX / sizeof(T); }83 84 template <class U>85 TEST_CONSTEXPR_CXX20 void construct(pointer p, U&& val) {86 if (stats_ != nullptr)87 ++stats_->construct_count;88 ::new (static_cast<void*>(p)) T(std::forward<U>(val));89 }90 91 TEST_CONSTEXPR_CXX14 void destroy(pointer p) {92 if (stats_ != nullptr) {93 ++stats_->destroy_count;94 }95 p->~T();96 }97};98 99// Move-only.100static_assert(!std::is_copy_constructible<std::__allocation_guard<A> >::value, "");101static_assert(std::is_move_constructible<std::__allocation_guard<A> >::value, "");102static_assert(!std::is_copy_assignable<std::__allocation_guard<A> >::value, "");103static_assert(std::is_move_assignable<std::__allocation_guard<A> >::value, "");104 105int main(int, char**) {106 const int size = 42;107 108 { // The constructor allocates using the given allocator.109 test_allocator_statistics stats;110 std::__allocation_guard<A> guard(A(&stats), size);111 assert(stats.alloc_count == 1);112 assert(guard.__get() != nullptr);113 }114 115 { // The destructor deallocates using the given allocator.116 test_allocator_statistics stats;117 {118 std::__allocation_guard<A> guard(A(&stats), size);119 assert(stats.alloc_count == 1);120 }121 assert(stats.alloc_count == 0);122 }123 124 { // `__release_ptr` prevents deallocation.125 test_allocator_statistics stats;126 A alloc(&stats);127 int* ptr = nullptr;128 {129 std::__allocation_guard<A> guard(alloc, size);130 assert(stats.alloc_count == 1);131 ptr = guard.__release_ptr();132 }133 assert(stats.alloc_count == 1);134 alloc.deallocate(ptr, size);135 }136 137 { // Using the move constructor doesn't lead to double deletion.138 test_allocator_statistics stats;139 {140 std::__allocation_guard<A> guard1(A(&stats), size);141 assert(stats.alloc_count == 1);142 auto* ptr1 = guard1.__get();143 144 std::__allocation_guard<A> guard2 = std::move(guard1);145 assert(stats.alloc_count == 1);146 assert(guard1.__get() == nullptr);147 assert(guard2.__get() == ptr1);148 }149 assert(stats.alloc_count == 0);150 }151 152 { // Using the move assignment operator doesn't lead to double deletion.153 using A2 = AssignableAllocator<int>;154 155 test_allocator_statistics stats;156 {157 std::__allocation_guard<A2> guard1(A2(stats), size);158 assert(stats.alloc_count == 1);159 std::__allocation_guard<A2> guard2(A2(stats), size);160 assert(stats.alloc_count == 2);161 auto* ptr1 = guard1.__get();162 163 guard2 = std::move(guard1);164 assert(stats.alloc_count == 1);165 assert(guard1.__get() == nullptr);166 assert(guard2.__get() == ptr1);167 }168 assert(stats.alloc_count == 0);169 }170 171 { // Self-assignment is a no-op.172 using A2 = AssignableAllocator<int>;173 174 test_allocator_statistics stats;175 {176 std::__allocation_guard<A2> guard(A2(stats), size);177 assert(stats.alloc_count == 1);178 auto* ptr = guard.__get();179 180 guard = std::move(guard);181 assert(stats.alloc_count == 1);182 assert(guard.__get() == ptr);183 }184 assert(stats.alloc_count == 0);185 }186 187 return 0;188}189