| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | // <memory> |
| 10 | |
| 11 | // shared_ptr |
| 12 | |
| 13 | // template<class T, class A, class... Args> |
| 14 | // shared_ptr<T> allocate_shared(const A& a, Args&&... args); |
| 15 | |
| 16 | // This test checks that allocator_traits::construct and allocator_traits::destroy |
| 17 | // are used in allocate_shared as requested for the resolution of LWG2070. Note |
| 18 | // that LWG2070 was resolved by P0674R1 (which is a C++20 paper), but we implement |
| 19 | // LWG issue resolutions as DRs per our policy. |
| 20 | |
| 21 | #include <cassert> |
| 22 | #include <cstddef> |
| 23 | #include <cstdint> |
| 24 | #include <memory> |
| 25 | #include <new> |
| 26 | #include <utility> |
| 27 | |
| 28 | #include "test_macros.h" |
| 29 | |
| 30 | static bool construct_called = false; |
| 31 | static bool destroy_called = false; |
| 32 | static unsigned allocator_id = 0; |
| 33 | |
| 34 | template <class T> |
| 35 | struct MyAllocator { |
| 36 | public: |
| 37 | typedef T value_type; |
| 38 | typedef T* pointer; |
| 39 | |
| 40 | unsigned id = 0; |
| 41 | |
| 42 | MyAllocator() = default; |
| 43 | MyAllocator(int i) : id(i) {} |
| 44 | |
| 45 | template <class U> |
| 46 | MyAllocator(MyAllocator<U> const& other) : id(other.id) {} |
| 47 | |
| 48 | pointer allocate(std::ptrdiff_t n) { |
| 49 | return pointer(static_cast<T*>(::operator new(n * sizeof(T)))); |
| 50 | } |
| 51 | |
| 52 | void deallocate(pointer p, std::ptrdiff_t) { return ::operator delete(p); } |
| 53 | |
| 54 | template <typename ...Args> |
| 55 | void construct(T* p, Args&& ...args) { |
| 56 | construct_called = true; |
| 57 | destroy_called = false; |
| 58 | allocator_id = id; |
| 59 | ::new (p) T(std::forward<Args>(args)...); |
| 60 | } |
| 61 | |
| 62 | void destroy(T* p) { |
| 63 | construct_called = false; |
| 64 | destroy_called = true; |
| 65 | allocator_id = id; |
| 66 | p->~T(); |
| 67 | } |
| 68 | }; |
| 69 | |
| 70 | struct Private; |
| 71 | |
| 72 | class Factory { |
| 73 | public: |
| 74 | static std::shared_ptr<Private> allocate(); |
| 75 | }; |
| 76 | |
| 77 | template <class T> |
| 78 | struct FactoryAllocator; |
| 79 | |
| 80 | struct Private { |
| 81 | int id; |
| 82 | |
| 83 | private: |
| 84 | friend FactoryAllocator<Private>; |
| 85 | Private(int i) : id(i) {} |
| 86 | ~Private() {} |
| 87 | }; |
| 88 | |
| 89 | template <class T> |
| 90 | struct FactoryAllocator : std::allocator<T> { |
| 91 | FactoryAllocator() = default; |
| 92 | |
| 93 | template <class T1> |
| 94 | FactoryAllocator(FactoryAllocator<T1>) {} |
| 95 | |
| 96 | template <class T1> |
| 97 | struct rebind { |
| 98 | typedef FactoryAllocator<T1> other; |
| 99 | }; |
| 100 | |
| 101 | void construct(void* p, int id) { ::new (p) Private(id); } |
| 102 | void destroy(Private* p) { p->~Private(); } |
| 103 | }; |
| 104 | |
| 105 | std::shared_ptr<Private> Factory::allocate() { |
| 106 | FactoryAllocator<Private> factory_alloc; |
| 107 | return std::allocate_shared<Private>(a: factory_alloc, args: 42); |
| 108 | } |
| 109 | |
| 110 | struct mchar { |
| 111 | char c; |
| 112 | }; |
| 113 | |
| 114 | struct Foo { |
| 115 | int val; |
| 116 | |
| 117 | Foo(int v) : val(v) {} |
| 118 | |
| 119 | Foo(Foo a, Foo b) : val(a.val + b.val) {} |
| 120 | }; |
| 121 | |
| 122 | void test_aligned(void* p, std::size_t align) { |
| 123 | assert(reinterpret_cast<std::uintptr_t>(p) % align == 0); |
| 124 | } |
| 125 | |
| 126 | int main(int, char**) { |
| 127 | { |
| 128 | std::shared_ptr<int> p = std::allocate_shared<int>(a: MyAllocator<int>()); |
| 129 | assert(construct_called); |
| 130 | } |
| 131 | assert(destroy_called); |
| 132 | { |
| 133 | std::shared_ptr<Foo> p = |
| 134 | std::allocate_shared<Foo>(a: MyAllocator<Foo>(), args: Foo(42), args: Foo(100)); |
| 135 | assert(construct_called); |
| 136 | assert(p->val == 142); |
| 137 | } |
| 138 | assert(destroy_called); |
| 139 | { // Make sure allocator is copied. |
| 140 | std::shared_ptr<int> p = std::allocate_shared<int>(a: MyAllocator<int>(3)); |
| 141 | assert(allocator_id == 3); |
| 142 | |
| 143 | allocator_id = 0; |
| 144 | } |
| 145 | assert(allocator_id == 3); |
| 146 | |
| 147 | { |
| 148 | std::shared_ptr<int> p = std::allocate_shared<int>(a: MyAllocator<int>(), args: 42); |
| 149 | assert(construct_called); |
| 150 | assert(*p == 42); |
| 151 | } |
| 152 | assert(destroy_called); |
| 153 | |
| 154 | { // Make sure allocator is properly re-bound. |
| 155 | std::shared_ptr<int> p = |
| 156 | std::allocate_shared<int>(a: MyAllocator<mchar>(), args: 42); |
| 157 | assert(construct_called); |
| 158 | assert(*p == 42); |
| 159 | } |
| 160 | assert(destroy_called); |
| 161 | |
| 162 | { |
| 163 | // Make sure that we call the correct allocator::construct. Private has a private constructor |
| 164 | // so the construct method must be called on its friend Factory's allocator |
| 165 | // (Factory::Allocator). |
| 166 | std::shared_ptr<Private> p = Factory().allocate(); |
| 167 | assert(p->id == 42); |
| 168 | } |
| 169 | |
| 170 | #if TEST_STD_VER >= 11 |
| 171 | { |
| 172 | struct Bar { |
| 173 | std::max_align_t y; |
| 174 | }; |
| 175 | |
| 176 | std::shared_ptr<Bar> p; |
| 177 | test_aligned(p.get(), alignof(Bar)); |
| 178 | } |
| 179 | #endif |
| 180 | |
| 181 | return 0; |
| 182 | } |
| 183 | |