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 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 
11 // <memory>
12 
13 // shared_ptr
14 
15 // template<class T, class A, class... Args>
16 // shared_ptr<T> allocate_shared(const A& a, Args&&... args);
17 
18 // This test checks that allocator_traits::construct is used in allocate_shared
19 // as requested in C++20 (via P0674R1).
20 
21 #include "test_macros.h"
22 
23 #include <memory>
24 #include <cassert>
25 
26 static bool construct_called = false;
27 static bool destroy_called = false;
28 static unsigned allocator_id = 0;
29 
30 template <class T>
31 struct MyAllocator {
32 public:
33   typedef T value_type;
34   typedef T* pointer;
35 
36   unsigned id = 0;
37 
38   MyAllocator() = default;
MyAllocatorMyAllocator39   MyAllocator(int i) : id(i) {}
40 
41   template <class U>
MyAllocatorMyAllocator42   MyAllocator(MyAllocator<U> const& other) : id(other.id){};
43 
allocateMyAllocator44   pointer allocate(std::ptrdiff_t n) {
45     return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
46   }
47 
deallocateMyAllocator48   void deallocate(pointer p, std::ptrdiff_t) { return ::operator delete(p); }
49 
50   template <typename ...Args>
constructMyAllocator51   void construct(T* p, Args&& ...args) {
52     construct_called = true;
53     destroy_called = false;
54     allocator_id = id;
55     ::new (p) T(std::forward<Args>(args)...);
56   }
57 
destroyMyAllocator58   void destroy(T* p) {
59     construct_called = false;
60     destroy_called = true;
61     allocator_id = id;
62     p->~T();
63   }
64 };
65 
66 struct Private;
67 
68 class Factory {
69 public:
70   static std::shared_ptr<Private> allocate();
71 };
72 
73 template <class T>
74 struct FactoryAllocator;
75 
76 struct Private {
77   int id;
78 
79 private:
80   friend FactoryAllocator<Private>;
PrivatePrivate81   Private(int i) : id(i) {}
~PrivatePrivate82   ~Private() {}
83 };
84 
85 template <class T>
86 struct FactoryAllocator : std::allocator<T> {
87   FactoryAllocator() = default;
88 
89   template <class T1>
FactoryAllocatorFactoryAllocator90   FactoryAllocator(FactoryAllocator<T1>) {}
91 
92   template <class T1>
93   struct rebind {
94     typedef FactoryAllocator<T1> other;
95   };
96 
constructFactoryAllocator97   void construct(void* p, int id) { ::new (p) Private(id); }
destroyFactoryAllocator98   void destroy(Private* p) { p->~Private(); }
99 };
100 
allocate()101 std::shared_ptr<Private> Factory::allocate() {
102   FactoryAllocator<Private> factory_alloc;
103   return std::allocate_shared<Private>(factory_alloc, 42);
104 }
105 
106 struct mchar {
107   char c;
108 };
109 
110 struct Foo {
111   int val;
112 
FooFoo113   Foo(int v) : val(v) {}
114 
FooFoo115   Foo(Foo a, Foo b) : val(a.val + b.val) {}
116 };
117 
118 struct Bar {
119   std::max_align_t y;
120 };
121 
test_aligned(void * p,size_t align)122 void test_aligned(void* p, size_t align) {
123   assert(reinterpret_cast<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   {
171     std::shared_ptr<Bar> p;
172     test_aligned(p.get(), alignof(Bar));
173   }
174 
175   return 0;
176 }
177