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;
MyAllocatorMyAllocator43 MyAllocator(int i) : id(i) {}
44
45 template <class U>
MyAllocatorMyAllocator46 MyAllocator(MyAllocator<U> const& other) : id(other.id) {}
47
allocateMyAllocator48 pointer allocate(std::ptrdiff_t n) {
49 return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
50 }
51
deallocateMyAllocator52 void deallocate(pointer p, std::ptrdiff_t) { return ::operator delete(p); }
53
54 template <typename ...Args>
constructMyAllocator55 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
destroyMyAllocator62 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>;
PrivatePrivate85 Private(int i) : id(i) {}
~PrivatePrivate86 ~Private() {}
87 };
88
89 template <class T>
90 struct FactoryAllocator : std::allocator<T> {
91 FactoryAllocator() = default;
92
93 template <class T1>
FactoryAllocatorFactoryAllocator94 FactoryAllocator(FactoryAllocator<T1>) {}
95
96 template <class T1>
97 struct rebind {
98 typedef FactoryAllocator<T1> other;
99 };
100
constructFactoryAllocator101 void construct(void* p, int id) { ::new (p) Private(id); }
destroyFactoryAllocator102 void destroy(Private* p) { p->~Private(); }
103 };
104
allocate()105 std::shared_ptr<Private> Factory::allocate() {
106 FactoryAllocator<Private> factory_alloc;
107 return std::allocate_shared<Private>(factory_alloc, 42);
108 }
109
110 struct mchar {
111 char c;
112 };
113
114 struct Foo {
115 int val;
116
FooFoo117 Foo(int v) : val(v) {}
118
FooFoo119 Foo(Foo a, Foo b) : val(a.val + b.val) {}
120 };
121
test_aligned(void * p,std::size_t align)122 void test_aligned(void* p, std::size_t align) {
123 assert(reinterpret_cast<std::uintptr_t>(p) % align == 0);
124 }
125
main(int,char **)126 int main(int, char**) {
127 {
128 std::shared_ptr<int> p = std::allocate_shared<int>(MyAllocator<int>());
129 assert(construct_called);
130 }
131 assert(destroy_called);
132 {
133 std::shared_ptr<Foo> p =
134 std::allocate_shared<Foo>(MyAllocator<Foo>(), Foo(42), 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>(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>(MyAllocator<int>(), 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>(MyAllocator<mchar>(), 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