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 #ifndef EXPERIMENTAL_ANY_HELPERS_H
9 #define EXPERIMENTAL_ANY_HELPERS_H
10 
11 #include <experimental/any>
12 #include <typeinfo>
13 #include <type_traits>
14 #include <cassert>
15 
16 #include "test_macros.h"
17 
18 template <class T>
19   struct IsSmallObject
20     : public std::integral_constant<bool
21         , sizeof(T) <= (sizeof(void*)*3)
22           && std::alignment_of<void*>::value
23              % std::alignment_of<T>::value == 0
24           && std::is_nothrow_move_constructible<T>::value
25         >
26   {};
27 
28 
29 // Return 'true' if 'Type' will be considered a small type by 'any'
30 template <class Type>
isSmallType()31 bool isSmallType() {
32 #if defined(_LIBCPP_VERSION)
33     return std::experimental::__any_imp::_IsSmallObject<Type>::value;
34 #else
35     return IsSmallObject<Type>::value;
36 #endif
37 
38 }
39 
40 // Assert that an object is empty. If the object used to contain an object
41 // of type 'LastType' check that it can no longer be accessed.
42 template <class LastType = int>
assertEmpty(std::experimental::any const & a)43 void assertEmpty(std::experimental::any const& a) {
44     assert(a.empty());
45     RTTI_ASSERT(a.type() == typeid(void));
46     assert(std::experimental::any_cast<LastType const>(&a) == nullptr);
47 }
48 
49 // Assert that an 'any' object stores the specified 'Type' and 'value'.
50 template <class Type>
51 _LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST
52 void assertContains(std::experimental::any const& a, int value = 1) {
53     assert(!a.empty());
54     RTTI_ASSERT(a.type() == typeid(Type));
55     assert(std::experimental::any_cast<Type const &>(a).value == value);
56 }
57 
58 // Modify the value of a "test type" stored within an any to the specified
59 // 'value'.
60 template <class Type>
61 _LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST
modifyValue(std::experimental::any & a,int value)62 void modifyValue(std::experimental::any& a, int value) {
63     assert(!a.empty());
64     RTTI_ASSERT(a.type() == typeid(Type));
65     std::experimental::any_cast<Type&>(a).value = value;
66 }
67 
68 // A test type that will trigger the small object optimization within 'any'.
69 template <int Dummy = 0>
70 struct small_type
71 {
72     static int count;
73     static int copied;
74     static int moved;
75     static int const_copied;
76     static int non_const_copied;
77 
resetsmall_type78     static void reset() {
79         small_type::copied = 0;
80         small_type::moved = 0;
81         small_type::const_copied = 0;
82         small_type::non_const_copied = 0;
83     }
84 
85     int value;
86 
small_typesmall_type87     explicit small_type(int val) : value(val) {
88         ++count;
89     }
90 
throwsmall_type91     small_type(small_type const & other) throw() {
92         value = other.value;
93         ++count;
94         ++copied;
95         ++const_copied;
96     }
97 
throwsmall_type98     small_type(small_type& other) throw() {
99         value = other.value;
100         ++count;
101         ++copied;
102         ++non_const_copied;
103     }
104 
throwsmall_type105     small_type(small_type && other) throw() {
106         value = other.value;
107         other.value = 0;
108         ++count;
109         ++moved;
110     }
111 
~small_typesmall_type112     ~small_type() {
113         value = -1;
114         --count;
115     }
116 
117 private:
118     small_type& operator=(small_type const&) = delete;
119     small_type& operator=(small_type&&) = delete;
120 };
121 
122 template <int Dummy>
123 int small_type<Dummy>::count = 0;
124 
125 template <int Dummy>
126 int small_type<Dummy>::copied = 0;
127 
128 template <int Dummy>
129 int small_type<Dummy>::moved = 0;
130 
131 template <int Dummy>
132 int small_type<Dummy>::const_copied = 0;
133 
134 template <int Dummy>
135 int small_type<Dummy>::non_const_copied = 0;
136 
137 typedef small_type<> small;
138 typedef small_type<1> small1;
139 typedef small_type<2> small2;
140 
141 
142 // A test type that will NOT trigger the small object optimization in any.
143 template <int Dummy = 0>
144 struct large_type
145 {
146     static int count;
147     static int copied;
148     static int moved;
149     static int const_copied;
150     static int non_const_copied;
151 
resetlarge_type152     static void reset() {
153         large_type::copied = 0;
154         large_type::moved  = 0;
155         large_type::const_copied = 0;
156         large_type::non_const_copied = 0;
157     }
158 
159     int value;
160 
large_typelarge_type161     large_type(int val) : value(val) {
162         ++count;
163         data[0] = 0;
164     }
165 
large_typelarge_type166     large_type(large_type const & other) {
167         value = other.value;
168         ++count;
169         ++copied;
170         ++const_copied;
171     }
172 
large_typelarge_type173     large_type(large_type & other) {
174         value = other.value;
175         ++count;
176         ++copied;
177         ++non_const_copied;
178     }
179 
large_typelarge_type180     large_type(large_type && other) {
181         value = other.value;
182         other.value = 0;
183         ++count;
184         ++moved;
185     }
186 
~large_typelarge_type187     ~large_type()  {
188         value = 0;
189         --count;
190     }
191 
192 private:
193     large_type& operator=(large_type const&) = delete;
194     large_type& operator=(large_type &&) = delete;
195     int data[10];
196 };
197 
198 template <int Dummy>
199 int large_type<Dummy>::count = 0;
200 
201 template <int Dummy>
202 int large_type<Dummy>::copied = 0;
203 
204 template <int Dummy>
205 int large_type<Dummy>::moved = 0;
206 
207 template <int Dummy>
208 int large_type<Dummy>::const_copied = 0;
209 
210 template <int Dummy>
211 int large_type<Dummy>::non_const_copied = 0;
212 
213 typedef large_type<> large;
214 typedef large_type<1> large1;
215 typedef large_type<2> large2;
216 
217 // The exception type thrown by 'small_throws_on_copy', 'large_throws_on_copy'
218 // and 'throws_on_move'.
219 struct my_any_exception {};
220 
throwMyAnyExpression()221 void throwMyAnyExpression() {
222 #if !defined(TEST_HAS_NO_EXCEPTIONS)
223         throw my_any_exception();
224 #else
225         assert(false && "Exceptions are disabled");
226 #endif
227 }
228 
229 // A test type that will trigger the small object optimization within 'any'.
230 // this type throws if it is copied.
231 struct small_throws_on_copy
232 {
233     static int count;
234     int value;
235 
valuesmall_throws_on_copy236     explicit small_throws_on_copy(int val = 0) : value(val) {
237         ++count;
238     }
239 
small_throws_on_copysmall_throws_on_copy240     small_throws_on_copy(small_throws_on_copy const &) {
241         throwMyAnyExpression();
242     }
243 
throwsmall_throws_on_copy244     small_throws_on_copy(small_throws_on_copy && other) throw() {
245         value = other.value;
246         ++count;
247     }
248 
~small_throws_on_copysmall_throws_on_copy249     ~small_throws_on_copy() {
250         --count;
251     }
252 private:
253     small_throws_on_copy& operator=(small_throws_on_copy const&) = delete;
254     small_throws_on_copy& operator=(small_throws_on_copy &&) = delete;
255 };
256 
257 int small_throws_on_copy::count = 0;
258 
259 // A test type that will NOT trigger the small object optimization within 'any'.
260 // this type throws if it is copied.
261 struct large_throws_on_copy
262 {
263     static int count;
264     int value = 0;
265 
valuelarge_throws_on_copy266     explicit large_throws_on_copy(int val = 0) : value(val) {
267         data[0] = 0;
268         ++count;
269     }
270 
large_throws_on_copylarge_throws_on_copy271     large_throws_on_copy(large_throws_on_copy const &) {
272          throwMyAnyExpression();
273     }
274 
throwlarge_throws_on_copy275     large_throws_on_copy(large_throws_on_copy && other) throw() {
276         value = other.value;
277         ++count;
278     }
279 
~large_throws_on_copylarge_throws_on_copy280     ~large_throws_on_copy() {
281         --count;
282     }
283 
284 private:
285     large_throws_on_copy& operator=(large_throws_on_copy const&) = delete;
286     large_throws_on_copy& operator=(large_throws_on_copy &&) = delete;
287     int data[10];
288 };
289 
290 int large_throws_on_copy::count = 0;
291 
292 // A test type that throws when it is moved. This object will NOT trigger
293 // the small object optimization in 'any'.
294 struct throws_on_move
295 {
296     static int count;
297     int value;
298 
valuethrows_on_move299     explicit throws_on_move(int val = 0) : value(val) { ++count; }
300 
throws_on_movethrows_on_move301     throws_on_move(throws_on_move const & other) {
302         value = other.value;
303         ++count;
304     }
305 
throws_on_movethrows_on_move306     throws_on_move(throws_on_move &&) {
307         throwMyAnyExpression();
308     }
309 
~throws_on_movethrows_on_move310     ~throws_on_move() {
311         --count;
312     }
313 private:
314     throws_on_move& operator=(throws_on_move const&) = delete;
315     throws_on_move& operator=(throws_on_move &&) = delete;
316 };
317 
318 int throws_on_move::count = 0;
319 
320 
321 #endif
322