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