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 | |