]>
Commit | Line | Data |
---|---|---|
53179144 BK |
1 | // -*- C++ -*- |
2 | ||
22977dce | 3 | // Copyright (C) 2009, 2010 Free Software Foundation, Inc. |
53179144 BK |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free | |
6 | // software; you can redistribute it and/or modify it under the terms | |
7 | // of the GNU General Public License as published by the Free Software | |
8 | // Foundation; either version 3, or (at your option) any later | |
9 | // version. | |
10 | ||
11 | // This library is distributed in the hope that it will be useful, but | |
12 | // WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | ||
16 | // You should have received a copy of the GNU General Public License along | |
17 | // with this library; see the file COPYING3. If not see | |
18 | // <http://www.gnu.org/licenses/>. | |
19 | ||
20 | #ifndef _GLIBCXX_EXCEPTION_SAFETY_H | |
21 | #define _GLIBCXX_EXCEPTION_SAFETY_H | |
22 | ||
23 | #include <testsuite_container_traits.h> | |
24 | #include <ext/throw_allocator.h> | |
25 | ||
26 | // Container requirement testing. | |
27 | namespace __gnu_test | |
28 | { | |
29 | // Base class for exception testing, contains utilities. | |
30 | struct setup_base | |
31 | { | |
32 | typedef std::size_t size_type; | |
33 | typedef std::uniform_int_distribution<size_type> distribution_type; | |
34 | typedef std::mt19937 engine_type; | |
35 | ||
36 | // Return randomly generated integer on range [0, __max_size]. | |
37 | static size_type | |
38 | generate(size_type __max_size) | |
39 | { | |
40 | // Make the generator static... | |
41 | const engine_type engine; | |
42 | const distribution_type distribution; | |
43 | static auto generator = std::bind(distribution, engine, | |
44 | std::placeholders::_1); | |
45 | ||
46 | // ... but set the range for this particular invocation here. | |
47 | const typename distribution_type::param_type p(0, __max_size); | |
48 | size_type random = generator(p); | |
49 | if (random < distribution.min() || random > distribution.max()) | |
50 | { | |
51 | std::string __s("setup_base::generate"); | |
52 | __s += "\n"; | |
53 | __s += "random number generated is: "; | |
54 | char buf[40]; | |
ba6a601c | 55 | __builtin_sprintf(buf, "%lu", (unsigned long)random); |
53179144 BK |
56 | __s += buf; |
57 | __s += " on range ["; | |
ba6a601c | 58 | __builtin_sprintf(buf, "%lu", (unsigned long)distribution.min()); |
53179144 BK |
59 | __s += buf; |
60 | __s += ", "; | |
ba6a601c | 61 | __builtin_sprintf(buf, "%lu", (unsigned long)distribution.max()); |
53179144 BK |
62 | __s += buf; |
63 | __s += "]\n"; | |
64 | std::__throw_out_of_range(__s.c_str()); | |
65 | } | |
66 | return random; | |
67 | } | |
68 | ||
69 | // Given an instantiating type, return a unique value. | |
70 | template<typename _Tp> | |
71 | struct generate_unique | |
72 | { | |
73 | typedef _Tp value_type; | |
74 | ||
75 | operator value_type() | |
76 | { | |
77 | static value_type __ret; | |
78 | ++__ret; | |
79 | return __ret; | |
80 | } | |
81 | }; | |
82 | ||
83 | // Partial specialization for pair. | |
84 | template<typename _Tp1, typename _Tp2> | |
85 | struct generate_unique<std::pair<const _Tp1, _Tp2>> | |
86 | { | |
87 | typedef _Tp1 first_type; | |
88 | typedef _Tp2 second_type; | |
89 | typedef std::pair<const _Tp1, _Tp2> pair_type; | |
90 | ||
91 | operator pair_type() | |
92 | { | |
93 | static first_type _S_1; | |
94 | static second_type _S_2; | |
95 | ++_S_1; | |
96 | ++_S_2; | |
97 | return pair_type(_S_1, _S_2); | |
98 | } | |
99 | }; | |
100 | ||
101 | // Partial specialization for throw_value | |
102 | template<typename _Cond> | |
103 | struct generate_unique<__gnu_cxx::throw_value_base<_Cond>> | |
104 | { | |
105 | typedef __gnu_cxx::throw_value_base<_Cond> value_type; | |
106 | ||
107 | operator value_type() | |
108 | { | |
109 | static size_t _S_i(0); | |
110 | return value_type(_S_i++); | |
111 | } | |
112 | }; | |
113 | ||
114 | ||
115 | // Construct container of size n directly. _Tp == container type. | |
116 | template<typename _Tp> | |
117 | struct make_container_base | |
118 | { | |
119 | _Tp _M_container; | |
120 | ||
121 | make_container_base() = default; | |
122 | make_container_base(const size_type n): _M_container(n) { } | |
123 | ||
124 | operator _Tp&() { return _M_container; } | |
125 | }; | |
126 | ||
127 | // Construct container of size n, via multiple insertions. For | |
128 | // associated and unordered types, unique value_type elements are | |
129 | // necessary. | |
130 | template<typename _Tp, bool = traits<_Tp>::is_mapped::value> | |
131 | struct make_insert_container_base | |
132 | : public make_container_base<_Tp> | |
133 | { | |
134 | using make_container_base<_Tp>::_M_container; | |
135 | typedef typename _Tp::value_type value_type; | |
136 | ||
137 | make_insert_container_base(const size_type n) | |
138 | { | |
139 | for (size_type i = 0; i < n; ++i) | |
140 | { | |
141 | value_type v = generate_unique<value_type>(); | |
142 | _M_container.insert(v); | |
143 | } | |
144 | assert(_M_container.size() == n); | |
145 | } | |
146 | }; | |
147 | ||
148 | template<typename _Tp> | |
149 | struct make_insert_container_base<_Tp, false> | |
150 | : public make_container_base<_Tp> | |
151 | { | |
152 | using make_container_base<_Tp>::_M_container; | |
153 | typedef typename _Tp::value_type value_type; | |
154 | ||
155 | make_insert_container_base(const size_type n) | |
156 | { | |
157 | for (size_type i = 0; i < n; ++i) | |
158 | { | |
159 | value_type v = generate_unique<value_type>(); | |
160 | _M_container.insert(_M_container.end(), v); | |
161 | } | |
162 | assert(_M_container.size() == n); | |
163 | } | |
164 | }; | |
165 | ||
166 | template<typename _Tp, bool = traits<_Tp>::has_size_type_constructor::value> | |
167 | struct make_container_n; | |
168 | ||
169 | // Specialization for non-associative types that have a constructor with | |
170 | // a size argument. | |
171 | template<typename _Tp> | |
172 | struct make_container_n<_Tp, true> | |
173 | : public make_container_base<_Tp> | |
174 | { | |
175 | make_container_n(const size_type n) : make_container_base<_Tp>(n) { } | |
176 | }; | |
177 | ||
178 | template<typename _Tp> | |
179 | struct make_container_n<_Tp, false> | |
180 | : public make_insert_container_base<_Tp> | |
181 | { | |
182 | make_container_n(const size_type n) | |
183 | : make_insert_container_base<_Tp>(n) { } | |
184 | }; | |
185 | ||
186 | ||
187 | // Randomly size and populate a given container reference. | |
188 | // NB: Responsibility for turning off exceptions lies with caller. | |
189 | template<typename _Tp, bool = traits<_Tp>::is_allocator_aware::value> | |
190 | struct populate | |
191 | { | |
192 | typedef _Tp container_type; | |
193 | typedef typename container_type::allocator_type allocator_type; | |
194 | typedef typename container_type::value_type value_type; | |
195 | ||
196 | populate(_Tp& __container) | |
197 | { | |
198 | const allocator_type a = __container.get_allocator(); | |
199 | ||
200 | // Size test container. | |
201 | const size_type max_elements = 100; | |
202 | size_type n = generate(max_elements); | |
203 | ||
204 | // Construct new container. | |
205 | make_container_n<container_type> made(n); | |
206 | container_type& tmp = made; | |
207 | std::swap(tmp, __container); | |
208 | } | |
209 | }; | |
210 | ||
211 | // Partial specialization, empty. | |
212 | template<typename _Tp> | |
213 | struct populate<_Tp, false> | |
214 | { | |
215 | populate(_Tp&) { } | |
216 | }; | |
217 | ||
218 | // Compare two containers for equivalence. | |
219 | // Right now, that means size. | |
220 | // Returns true if equal, throws if not. | |
221 | template<typename _Tp> | |
222 | static bool | |
223 | compare(const _Tp& __control, const _Tp& __test) | |
224 | { | |
225 | // Make sure test container is in a consistent state, as | |
226 | // compared to the control container. | |
227 | // NB: Should be equivalent to __test != __control, but | |
228 | // computed without equivalence operators | |
229 | const size_type szt = std::distance(__test.begin(), __test.end()); | |
230 | const size_type szc = std::distance(__control.begin(), | |
231 | __control.end()); | |
232 | bool __equal_size = szt == szc; | |
233 | ||
234 | // Should test iterator validity before and after exception. | |
235 | bool __equal_it = std::equal(__test.begin(), __test.end(), | |
236 | __control.begin()); | |
237 | ||
238 | if (!__equal_size || !__equal_it) | |
239 | throw std::logic_error("setup_base::compare containers not equal"); | |
240 | ||
241 | return true; | |
242 | } | |
243 | }; | |
244 | ||
245 | ||
246 | // Containing structure holding functors. | |
247 | struct functor_base : public setup_base | |
248 | { | |
249 | // Abstract the erase function. | |
250 | template<typename _Tp> | |
251 | struct erase_base | |
252 | { | |
253 | typedef typename _Tp::iterator iterator; | |
254 | ||
255 | iterator (_Tp::* _F_erase_point)(iterator); | |
256 | iterator (_Tp::* _F_erase_range)(iterator, iterator); | |
257 | ||
258 | erase_base() | |
259 | : _F_erase_point(&_Tp::erase), _F_erase_range(&_Tp::erase) { } | |
260 | }; | |
261 | ||
262 | // Specialization, as forward_list has erase_after. | |
263 | template<typename _Tp1, typename _Tp2> | |
264 | struct erase_base<std::forward_list<_Tp1, _Tp2>> | |
265 | { | |
266 | typedef std::forward_list<_Tp1, _Tp2> container_type; | |
267 | typedef typename container_type::iterator iterator; | |
268 | typedef typename container_type::const_iterator const_iterator; | |
269 | ||
270 | void (container_type::* _F_erase_point)(const_iterator); | |
271 | void (container_type::* _F_erase_range)(const_iterator, const_iterator); | |
272 | ||
273 | erase_base() | |
274 | : _F_erase_point(&container_type::erase_after), | |
275 | _F_erase_range(&container_type::erase_after) { } | |
276 | }; | |
277 | ||
3b2524b1 PC |
278 | // Specializations for the unordered containers. |
279 | template<typename _Tp1, typename _Tp2, typename _Tp3, | |
280 | typename _Tp4, typename _Tp5> | |
281 | struct erase_base<std::unordered_map<_Tp1, _Tp2, _Tp3, _Tp4, _Tp5>> | |
282 | { | |
283 | typedef std::unordered_map<_Tp1, _Tp2, _Tp3, _Tp4, _Tp5> | |
284 | container_type; | |
285 | typedef typename container_type::iterator iterator; | |
286 | typedef typename container_type::const_iterator const_iterator; | |
287 | ||
d723ced2 PC |
288 | iterator (container_type::* _F_erase_point)(const_iterator); |
289 | iterator (container_type::* _F_erase_range)(const_iterator, | |
290 | const_iterator); | |
3b2524b1 PC |
291 | |
292 | erase_base() | |
293 | : _F_erase_point(&container_type::erase), | |
294 | _F_erase_range(&container_type::erase) { } | |
295 | }; | |
296 | ||
297 | template<typename _Tp1, typename _Tp2, typename _Tp3, | |
298 | typename _Tp4, typename _Tp5> | |
299 | struct erase_base<std::unordered_multimap<_Tp1, _Tp2, _Tp3, | |
300 | _Tp4, _Tp5>> | |
301 | { | |
302 | typedef std::unordered_multimap<_Tp1, _Tp2, _Tp3, _Tp4, _Tp5> | |
303 | container_type; | |
304 | typedef typename container_type::iterator iterator; | |
305 | typedef typename container_type::const_iterator const_iterator; | |
306 | ||
d723ced2 PC |
307 | iterator (container_type::* _F_erase_point)(const_iterator); |
308 | iterator (container_type::* _F_erase_range)(const_iterator, | |
309 | const_iterator); | |
3b2524b1 PC |
310 | |
311 | erase_base() | |
312 | : _F_erase_point(&container_type::erase), | |
313 | _F_erase_range(&container_type::erase) { } | |
314 | }; | |
315 | ||
316 | template<typename _Tp1, typename _Tp2, typename _Tp3, typename _Tp4> | |
317 | struct erase_base<std::unordered_set<_Tp1, _Tp2, _Tp3, _Tp4>> | |
318 | { | |
319 | typedef std::unordered_set<_Tp1, _Tp2, _Tp3, _Tp4> | |
320 | container_type; | |
321 | typedef typename container_type::iterator iterator; | |
322 | typedef typename container_type::const_iterator const_iterator; | |
323 | ||
d723ced2 PC |
324 | iterator (container_type::* _F_erase_point)(const_iterator); |
325 | iterator (container_type::* _F_erase_range)(const_iterator, | |
326 | const_iterator); | |
3b2524b1 PC |
327 | |
328 | erase_base() | |
329 | : _F_erase_point(&container_type::erase), | |
330 | _F_erase_range(&container_type::erase) { } | |
331 | }; | |
332 | ||
333 | template<typename _Tp1, typename _Tp2, typename _Tp3, typename _Tp4> | |
334 | struct erase_base<std::unordered_multiset<_Tp1, _Tp2, _Tp3, _Tp4>> | |
335 | { | |
336 | typedef std::unordered_multiset<_Tp1, _Tp2, _Tp3, _Tp4> | |
337 | container_type; | |
338 | typedef typename container_type::iterator iterator; | |
339 | typedef typename container_type::const_iterator const_iterator; | |
340 | ||
d723ced2 PC |
341 | iterator (container_type::* _F_erase_point)(const_iterator); |
342 | iterator (container_type::* _F_erase_range)(const_iterator, | |
343 | const_iterator); | |
3b2524b1 PC |
344 | |
345 | erase_base() | |
346 | : _F_erase_point(&container_type::erase), | |
347 | _F_erase_range(&container_type::erase) { } | |
348 | }; | |
349 | ||
53179144 BK |
350 | template<typename _Tp, bool = traits<_Tp>::has_erase::value> |
351 | struct erase_point : public erase_base<_Tp> | |
352 | { | |
353 | using erase_base<_Tp>::_F_erase_point; | |
354 | ||
355 | void | |
356 | operator()(_Tp& __container) | |
357 | { | |
358 | try | |
359 | { | |
360 | // NB: Should be equivalent to size() member function, but | |
361 | // computed with begin() and end(). | |
362 | const size_type sz = std::distance(__container.begin(), | |
363 | __container.end()); | |
364 | ||
365 | // NB: Lowest common denominator: use forward iterator operations. | |
366 | auto i = __container.begin(); | |
367 | std::advance(i, generate(sz)); | |
368 | ||
369 | // Makes it easier to think of this as __container.erase(i) | |
370 | (__container.*_F_erase_point)(i); | |
371 | } | |
372 | catch(const __gnu_cxx::forced_error&) | |
373 | { throw; } | |
374 | } | |
375 | }; | |
376 | ||
377 | // Specialization, empty. | |
378 | template<typename _Tp> | |
379 | struct erase_point<_Tp, false> | |
380 | { | |
381 | void | |
382 | operator()(_Tp&) { } | |
383 | }; | |
384 | ||
385 | ||
386 | template<typename _Tp, bool = traits<_Tp>::has_erase::value> | |
387 | struct erase_range : public erase_base<_Tp> | |
388 | { | |
389 | using erase_base<_Tp>::_F_erase_range; | |
390 | ||
391 | void | |
392 | operator()(_Tp& __container) | |
393 | { | |
394 | try | |
395 | { | |
396 | const size_type sz = std::distance(__container.begin(), | |
397 | __container.end()); | |
398 | size_type s1 = generate(sz); | |
399 | size_type s2 = generate(sz); | |
400 | auto i1 = __container.begin(); | |
401 | auto i2 = __container.begin(); | |
402 | std::advance(i1, std::min(s1, s2)); | |
403 | std::advance(i2, std::max(s1, s2)); | |
404 | ||
405 | // Makes it easier to think of this as __container.erase(i1, i2). | |
406 | (__container.*_F_erase_range)(i1, i2); | |
407 | } | |
408 | catch(const __gnu_cxx::forced_error&) | |
409 | { throw; } | |
410 | } | |
411 | }; | |
412 | ||
413 | // Specialization, empty. | |
414 | template<typename _Tp> | |
415 | struct erase_range<_Tp, false> | |
416 | { | |
417 | void | |
418 | operator()(_Tp&) { } | |
419 | }; | |
420 | ||
421 | ||
422 | template<typename _Tp, bool = traits<_Tp>::has_push_pop::value> | |
423 | struct pop_front | |
424 | { | |
425 | void | |
426 | operator()(_Tp& __container) | |
427 | { | |
428 | try | |
429 | { | |
430 | __container.pop_front(); | |
431 | } | |
432 | catch(const __gnu_cxx::forced_error&) | |
433 | { throw; } | |
434 | } | |
435 | }; | |
436 | ||
437 | // Specialization, empty. | |
438 | template<typename _Tp> | |
439 | struct pop_front<_Tp, false> | |
440 | { | |
441 | void | |
442 | operator()(_Tp&) { } | |
443 | }; | |
444 | ||
445 | ||
446 | template<typename _Tp, bool = traits<_Tp>::has_push_pop::value | |
447 | && traits<_Tp>::is_reversible::value> | |
448 | struct pop_back | |
449 | { | |
450 | void | |
451 | operator()(_Tp& __container) | |
452 | { | |
453 | try | |
454 | { | |
455 | __container.pop_back(); | |
456 | } | |
457 | catch(const __gnu_cxx::forced_error&) | |
458 | { throw; } | |
459 | } | |
460 | }; | |
461 | ||
462 | // Specialization, empty. | |
463 | template<typename _Tp> | |
464 | struct pop_back<_Tp, false> | |
465 | { | |
466 | void | |
467 | operator()(_Tp&) { } | |
468 | }; | |
469 | ||
470 | ||
471 | template<typename _Tp, bool = traits<_Tp>::has_push_pop::value> | |
472 | struct push_front | |
473 | { | |
474 | typedef _Tp container_type; | |
475 | typedef typename container_type::value_type value_type; | |
476 | ||
477 | void | |
478 | operator()(_Tp& __test) | |
479 | { | |
480 | try | |
481 | { | |
482 | const value_type cv = generate_unique<value_type>(); | |
483 | __test.push_front(cv); | |
484 | } | |
485 | catch(const __gnu_cxx::forced_error&) | |
486 | { throw; } | |
487 | } | |
488 | ||
489 | // Assumes containers start out equivalent. | |
490 | void | |
491 | operator()(_Tp& __control, _Tp& __test) | |
492 | { | |
493 | try | |
494 | { | |
495 | const value_type cv = generate_unique<value_type>(); | |
496 | __test.push_front(cv); | |
497 | } | |
498 | catch(const __gnu_cxx::forced_error&) | |
499 | { throw; } | |
500 | } | |
501 | }; | |
502 | ||
503 | // Specialization, empty. | |
504 | template<typename _Tp> | |
505 | struct push_front<_Tp, false> | |
506 | { | |
507 | void | |
508 | operator()(_Tp&) { } | |
509 | ||
510 | void | |
511 | operator()(_Tp&, _Tp&) { } | |
512 | }; | |
513 | ||
514 | ||
515 | template<typename _Tp, bool = traits<_Tp>::has_push_pop::value | |
516 | && traits<_Tp>::is_reversible::value> | |
517 | struct push_back | |
518 | { | |
519 | typedef _Tp container_type; | |
520 | typedef typename container_type::value_type value_type; | |
521 | ||
522 | void | |
523 | operator()(_Tp& __test) | |
524 | { | |
525 | try | |
526 | { | |
527 | const value_type cv = generate_unique<value_type>(); | |
528 | __test.push_back(cv); | |
529 | } | |
530 | catch(const __gnu_cxx::forced_error&) | |
531 | { throw; } | |
532 | } | |
533 | ||
534 | // Assumes containers start out equivalent. | |
535 | void | |
536 | operator()(_Tp& __control, _Tp& __test) | |
537 | { | |
538 | try | |
539 | { | |
540 | const value_type cv = generate_unique<value_type>(); | |
541 | __test.push_back(cv); | |
542 | } | |
543 | catch(const __gnu_cxx::forced_error&) | |
544 | { throw; } | |
545 | } | |
546 | }; | |
547 | ||
548 | // Specialization, empty. | |
549 | template<typename _Tp> | |
550 | struct push_back<_Tp, false> | |
551 | { | |
552 | void | |
553 | operator()(_Tp&) { } | |
554 | ||
555 | void | |
556 | operator()(_Tp&, _Tp&) { } | |
557 | }; | |
558 | ||
559 | ||
560 | // Abstract the insert function into two parts: | |
561 | // 1, insert_base_functions == holds function pointer | |
562 | // 2, insert_base == links function pointer to class insert method | |
563 | template<typename _Tp> | |
564 | struct insert_base | |
565 | { | |
566 | typedef typename _Tp::iterator iterator; | |
567 | typedef typename _Tp::value_type value_type; | |
568 | ||
569 | iterator (_Tp::* _F_insert_point)(iterator, const value_type&); | |
570 | ||
571 | insert_base() : _F_insert_point(&_Tp::insert) { } | |
572 | }; | |
573 | ||
574 | // Specialization, as string insertion has a different signature. | |
575 | template<typename _Tp1, typename _Tp2, typename _Tp3> | |
576 | struct insert_base<std::basic_string<_Tp1, _Tp2, _Tp3>> | |
577 | { | |
578 | typedef std::basic_string<_Tp1, _Tp2, _Tp3> container_type; | |
579 | typedef typename container_type::iterator iterator; | |
580 | typedef typename container_type::value_type value_type; | |
581 | ||
582 | iterator (container_type::* _F_insert_point)(iterator, value_type); | |
583 | ||
584 | insert_base() : _F_insert_point(&container_type::insert) { } | |
585 | }; | |
586 | ||
22977dce PC |
587 | template<typename _Tp1, typename _Tp2, typename _Tp3, |
588 | template <typename, typename, typename> class _Tp4> | |
589 | struct insert_base<__gnu_cxx::__versa_string<_Tp1, _Tp2, _Tp3, _Tp4>> | |
53179144 | 590 | { |
22977dce PC |
591 | typedef __gnu_cxx::__versa_string<_Tp1, _Tp2, _Tp3, _Tp4> |
592 | container_type; | |
53179144 BK |
593 | typedef typename container_type::iterator iterator; |
594 | typedef typename container_type::value_type value_type; | |
595 | ||
596 | iterator (container_type::* _F_insert_point)(iterator, value_type); | |
597 | ||
598 | insert_base() : _F_insert_point(&container_type::insert) { } | |
599 | }; | |
600 | ||
601 | // Specialization, as forward_list insertion has a different signature. | |
602 | template<typename _Tp1, typename _Tp2> | |
603 | struct insert_base<std::forward_list<_Tp1, _Tp2>> | |
604 | { | |
605 | typedef std::forward_list<_Tp1, _Tp2> container_type; | |
606 | typedef typename container_type::iterator iterator; | |
3b2524b1 | 607 | typedef typename container_type::const_iterator const_iterator; |
53179144 BK |
608 | typedef typename container_type::value_type value_type; |
609 | ||
610 | iterator (container_type::* _F_insert_point)(const_iterator, | |
3b2524b1 | 611 | const value_type&); |
53179144 BK |
612 | |
613 | insert_base() : _F_insert_point(&container_type::insert_after) { } | |
614 | }; | |
615 | ||
3b2524b1 PC |
616 | // Likewise for the unordered containers. |
617 | template<typename _Tp1, typename _Tp2, typename _Tp3, | |
618 | typename _Tp4, typename _Tp5> | |
619 | struct insert_base<std::unordered_map<_Tp1, _Tp2, _Tp3, _Tp4, _Tp5>> | |
620 | { | |
621 | typedef std::unordered_map<_Tp1, _Tp2, _Tp3, _Tp4, _Tp5> | |
622 | container_type; | |
623 | typedef typename container_type::iterator iterator; | |
624 | typedef typename container_type::const_iterator const_iterator; | |
625 | typedef typename container_type::value_type value_type; | |
626 | ||
627 | iterator (container_type::* _F_insert_point)(const_iterator, | |
628 | const value_type&); | |
629 | ||
630 | insert_base() : _F_insert_point(&container_type::insert) { } | |
631 | }; | |
632 | ||
633 | template<typename _Tp1, typename _Tp2, typename _Tp3, | |
634 | typename _Tp4, typename _Tp5> | |
635 | struct insert_base<std::unordered_multimap<_Tp1, _Tp2, _Tp3, | |
636 | _Tp4, _Tp5>> | |
637 | { | |
638 | typedef std::unordered_multimap<_Tp1, _Tp2, _Tp3, _Tp4, _Tp5> | |
639 | container_type; | |
640 | typedef typename container_type::iterator iterator; | |
641 | typedef typename container_type::const_iterator const_iterator; | |
642 | typedef typename container_type::value_type value_type; | |
643 | ||
644 | iterator (container_type::* _F_insert_point)(const_iterator, | |
645 | const value_type&); | |
646 | ||
647 | insert_base() : _F_insert_point(&container_type::insert) { } | |
648 | }; | |
649 | ||
650 | template<typename _Tp1, typename _Tp2, typename _Tp3, typename _Tp4> | |
651 | struct insert_base<std::unordered_set<_Tp1, _Tp2, _Tp3, _Tp4>> | |
652 | { | |
653 | typedef std::unordered_set<_Tp1, _Tp2, _Tp3, _Tp4> | |
654 | container_type; | |
655 | typedef typename container_type::iterator iterator; | |
656 | typedef typename container_type::const_iterator const_iterator; | |
657 | typedef typename container_type::value_type value_type; | |
658 | ||
659 | iterator (container_type::* _F_insert_point)(const_iterator, | |
660 | const value_type&); | |
661 | ||
662 | insert_base() : _F_insert_point(&container_type::insert) { } | |
663 | }; | |
664 | ||
665 | template<typename _Tp1, typename _Tp2, typename _Tp3, typename _Tp4> | |
666 | struct insert_base<std::unordered_multiset<_Tp1, _Tp2, _Tp3, _Tp4>> | |
667 | { | |
668 | typedef std::unordered_multiset<_Tp1, _Tp2, _Tp3, _Tp4> | |
669 | container_type; | |
670 | typedef typename container_type::iterator iterator; | |
671 | typedef typename container_type::const_iterator const_iterator; | |
672 | typedef typename container_type::value_type value_type; | |
673 | ||
674 | iterator (container_type::* _F_insert_point)(const_iterator, | |
675 | const value_type&); | |
676 | ||
677 | insert_base() : _F_insert_point(&container_type::insert) { } | |
678 | }; | |
679 | ||
53179144 BK |
680 | template<typename _Tp, bool = traits<_Tp>::has_insert::value> |
681 | struct insert_point : public insert_base<_Tp> | |
682 | { | |
683 | typedef _Tp container_type; | |
684 | typedef typename container_type::value_type value_type; | |
685 | using insert_base<_Tp>::_F_insert_point; | |
686 | ||
687 | void | |
688 | operator()(_Tp& __test) | |
689 | { | |
690 | try | |
691 | { | |
692 | const value_type cv = generate_unique<value_type>(); | |
693 | const size_type sz = std::distance(__test.begin(), __test.end()); | |
694 | size_type s = generate(sz); | |
695 | auto i = __test.begin(); | |
696 | std::advance(i, s); | |
697 | (__test.*_F_insert_point)(i, cv); | |
698 | } | |
699 | catch(const __gnu_cxx::forced_error&) | |
700 | { throw; } | |
701 | } | |
702 | ||
703 | // Assumes containers start out equivalent. | |
704 | void | |
705 | operator()(_Tp& __control, _Tp& __test) | |
706 | { | |
707 | try | |
708 | { | |
709 | const value_type cv = generate_unique<value_type>(); | |
710 | const size_type sz = std::distance(__test.begin(), __test.end()); | |
711 | size_type s = generate(sz); | |
712 | auto i = __test.begin(); | |
713 | std::advance(i, s); | |
714 | (__test.*_F_insert_point)(i, cv); | |
715 | } | |
716 | catch(const __gnu_cxx::forced_error&) | |
717 | { throw; } | |
718 | } | |
719 | }; | |
720 | ||
721 | // Specialization, empty. | |
722 | template<typename _Tp> | |
723 | struct insert_point<_Tp, false> | |
724 | { | |
725 | void | |
726 | operator()(_Tp&) { } | |
727 | ||
728 | void | |
729 | operator()(_Tp&, _Tp&) { } | |
730 | }; | |
731 | ||
732 | ||
733 | template<typename _Tp, bool = traits<_Tp>::is_associative::value | |
734 | || traits<_Tp>::is_unordered::value> | |
735 | struct clear | |
736 | { | |
737 | void | |
738 | operator()(_Tp& __container) | |
739 | { | |
740 | try | |
741 | { | |
742 | __container.clear(); | |
743 | } | |
744 | catch(const __gnu_cxx::forced_error&) | |
745 | { throw; } | |
746 | } | |
747 | }; | |
748 | ||
749 | // Specialization, empty. | |
750 | template<typename _Tp> | |
751 | struct clear<_Tp, false> | |
752 | { | |
753 | void | |
754 | operator()(_Tp&) { } | |
755 | }; | |
756 | ||
757 | ||
758 | template<typename _Tp, bool = traits<_Tp>::is_unordered::value> | |
759 | struct rehash | |
760 | { | |
761 | void | |
762 | operator()(_Tp& __test) | |
763 | { | |
764 | try | |
765 | { | |
766 | size_type s = generate(__test.bucket_count()); | |
767 | __test.rehash(s); | |
768 | } | |
769 | catch(const __gnu_cxx::forced_error&) | |
770 | { throw; } | |
771 | } | |
772 | ||
773 | void | |
774 | operator()(_Tp& __control, _Tp& __test) | |
775 | { | |
776 | try | |
777 | { | |
778 | size_type s = generate(__test.bucket_count()); | |
779 | __test.rehash(s); | |
780 | } | |
781 | catch(const __gnu_cxx::forced_error&) | |
782 | { | |
783 | // Also check hash status. | |
784 | bool fail(false); | |
785 | if (__control.load_factor() != __test.load_factor()) | |
786 | fail = true; | |
787 | if (__control.max_load_factor() != __test.max_load_factor()) | |
788 | fail = true; | |
789 | if (__control.bucket_count() != __test.bucket_count()) | |
790 | fail = true; | |
791 | if (__control.max_bucket_count() != __test.max_bucket_count()) | |
792 | fail = true; | |
793 | ||
794 | if (fail) | |
795 | { | |
796 | char buf[40]; | |
797 | std::string __s("setup_base::rehash " | |
798 | "containers not equal"); | |
799 | __s += "\n"; | |
800 | __s += "\n"; | |
801 | __s += "\t\t\tcontrol : test"; | |
802 | __s += "\n"; | |
803 | __s += "load_factor\t\t"; | |
804 | __builtin_sprintf(buf, "%lu", __control.load_factor()); | |
805 | __s += buf; | |
806 | __s += " : "; | |
807 | __builtin_sprintf(buf, "%lu", __test.load_factor()); | |
808 | __s += buf; | |
809 | __s += "\n"; | |
810 | ||
811 | __s += "max_load_factor\t\t"; | |
812 | __builtin_sprintf(buf, "%lu", __control.max_load_factor()); | |
813 | __s += buf; | |
814 | __s += " : "; | |
815 | __builtin_sprintf(buf, "%lu", __test.max_load_factor()); | |
816 | __s += buf; | |
817 | __s += "\n"; | |
818 | ||
819 | __s += "bucket_count\t\t"; | |
820 | __builtin_sprintf(buf, "%lu", __control.bucket_count()); | |
821 | __s += buf; | |
822 | __s += " : "; | |
823 | __builtin_sprintf(buf, "%lu", __test.bucket_count()); | |
824 | __s += buf; | |
825 | __s += "\n"; | |
826 | ||
827 | __s += "max_bucket_count\t"; | |
828 | __builtin_sprintf(buf, "%lu", __control.max_bucket_count()); | |
829 | __s += buf; | |
830 | __s += " : "; | |
831 | __builtin_sprintf(buf, "%lu", __test.max_bucket_count()); | |
832 | __s += buf; | |
833 | __s += "\n"; | |
834 | ||
835 | std::__throw_logic_error(__s.c_str()); | |
836 | } | |
837 | } | |
838 | } | |
839 | }; | |
840 | ||
841 | // Specialization, empty. | |
842 | template<typename _Tp> | |
843 | struct rehash<_Tp, false> | |
844 | { | |
845 | void | |
846 | operator()(_Tp&) { } | |
847 | ||
848 | void | |
849 | operator()(_Tp&, _Tp&) { } | |
850 | }; | |
851 | ||
852 | ||
853 | template<typename _Tp> | |
854 | struct swap | |
855 | { | |
856 | _Tp _M_other; | |
857 | ||
858 | void | |
859 | operator()(_Tp& __container) | |
860 | { | |
861 | try | |
862 | { | |
863 | __container.swap(_M_other); | |
864 | } | |
865 | catch(const __gnu_cxx::forced_error&) | |
866 | { throw; } | |
867 | } | |
868 | }; | |
869 | ||
870 | ||
871 | template<typename _Tp> | |
872 | struct iterator_operations | |
873 | { | |
874 | typedef _Tp container_type; | |
875 | typedef typename container_type::iterator iterator; | |
876 | ||
877 | void | |
878 | operator()(_Tp& __container) | |
879 | { | |
880 | try | |
881 | { | |
882 | // Any will do. | |
883 | iterator i = __container.begin(); | |
884 | iterator __attribute__((unused)) icopy(i); | |
885 | iterator __attribute__((unused)) iassign = i; | |
886 | } | |
887 | catch(const __gnu_cxx::forced_error&) | |
888 | { throw; } | |
889 | } | |
890 | }; | |
891 | ||
892 | ||
893 | template<typename _Tp> | |
894 | struct const_iterator_operations | |
895 | { | |
896 | typedef _Tp container_type; | |
897 | typedef typename container_type::const_iterator const_iterator; | |
898 | ||
899 | void | |
900 | operator()(_Tp& __container) | |
901 | { | |
902 | try | |
903 | { | |
904 | // Any will do. | |
905 | const_iterator i = __container.begin(); | |
906 | const_iterator __attribute__((unused)) icopy(i); | |
907 | const_iterator __attribute__((unused)) iassign = i; | |
908 | } | |
909 | catch(const __gnu_cxx::forced_error&) | |
910 | { throw; } | |
911 | } | |
912 | }; | |
913 | }; | |
914 | ||
915 | // Base class for exception tests. | |
916 | template<typename _Tp> | |
917 | struct test_base: public functor_base | |
918 | { | |
919 | typedef _Tp container_type; | |
920 | ||
921 | typedef functor_base base_type; | |
922 | typedef populate<container_type> populate; | |
923 | typedef make_container_n<container_type> make_container_n; | |
924 | ||
925 | typedef clear<container_type> clear; | |
926 | typedef erase_point<container_type> erase_point; | |
927 | typedef erase_range<container_type> erase_range; | |
928 | typedef insert_point<container_type> insert_point; | |
929 | typedef pop_front<container_type> pop_front; | |
930 | typedef pop_back<container_type> pop_back; | |
931 | typedef push_front<container_type> push_front; | |
932 | typedef push_back<container_type> push_back; | |
933 | typedef rehash<container_type> rehash; | |
934 | typedef swap<container_type> swap; | |
935 | typedef iterator_operations<container_type> iterator_ops; | |
936 | typedef const_iterator_operations<container_type> const_iterator_ops; | |
937 | ||
938 | using base_type::compare; | |
939 | ||
940 | // Functor objects. | |
941 | clear _M_clear; | |
942 | erase_point _M_erasep; | |
943 | erase_range _M_eraser; | |
944 | insert_point _M_insertp; | |
945 | pop_front _M_popf; | |
946 | pop_back _M_popb; | |
947 | push_front _M_pushf; | |
948 | push_back _M_pushb; | |
949 | rehash _M_rehash; | |
950 | swap _M_swap; | |
951 | ||
952 | iterator_ops _M_iops; | |
953 | const_iterator_ops _M_ciops; | |
954 | }; | |
955 | ||
956 | ||
957 | // Run through all member functions for basic exception safety | |
958 | // guarantee: no resource leaks when exceptions are thrown. | |
959 | // | |
960 | // Types of resources checked: memory. | |
961 | // | |
962 | // For each member function, use throw_value and throw_allocator as | |
963 | // value_type and allocator_type to force potential exception safety | |
964 | // errors. | |
965 | // | |
966 | // NB: Assumes | |
967 | // _Tp::value_type is __gnu_cxx::throw_value_* | |
968 | // _Tp::allocator_type is __gnu_cxx::throw_allocator_* | |
969 | // And that the _Cond template parameter for them both is | |
970 | // __gnu_cxx::limit_condition. | |
971 | template<typename _Tp> | |
972 | struct basic_safety : public test_base<_Tp> | |
973 | { | |
974 | typedef _Tp container_type; | |
975 | typedef test_base<container_type> base_type; | |
976 | typedef typename base_type::populate populate; | |
977 | typedef std::function<void(container_type&)> function_type; | |
978 | typedef __gnu_cxx::limit_condition condition_type; | |
979 | ||
980 | using base_type::generate; | |
981 | ||
982 | container_type _M_container; | |
983 | std::vector<function_type> _M_functions; | |
984 | ||
985 | basic_safety() { run(); } | |
986 | ||
987 | void | |
988 | run() | |
989 | { | |
990 | // Setup. | |
991 | condition_type::never_adjustor off; | |
992 | ||
993 | // Construct containers. | |
994 | populate p1(_M_container); | |
995 | populate p2(base_type::_M_swap._M_other); | |
996 | ||
997 | // Construct list of member functions to exercise. | |
998 | _M_functions.push_back(function_type(base_type::_M_iops)); | |
999 | _M_functions.push_back(function_type(base_type::_M_ciops)); | |
1000 | ||
1001 | _M_functions.push_back(function_type(base_type::_M_erasep)); | |
1002 | _M_functions.push_back(function_type(base_type::_M_eraser)); | |
1003 | _M_functions.push_back(function_type(base_type::_M_insertp)); | |
1004 | _M_functions.push_back(function_type(base_type::_M_popf)); | |
1005 | _M_functions.push_back(function_type(base_type::_M_popb)); | |
1006 | _M_functions.push_back(function_type(base_type::_M_pushf)); | |
1007 | _M_functions.push_back(function_type(base_type::_M_pushb)); | |
1008 | _M_functions.push_back(function_type(base_type::_M_rehash)); | |
1009 | _M_functions.push_back(function_type(base_type::_M_swap)); | |
1010 | ||
1011 | // Last. | |
1012 | _M_functions.push_back(function_type(base_type::_M_clear)); | |
1013 | ||
1014 | // Run tests. | |
53179144 BK |
1015 | for (auto i = _M_functions.begin(); i != _M_functions.end(); ++i) |
1016 | { | |
1017 | function_type& f = *i; | |
1018 | run_steps_to_limit(f); | |
1019 | } | |
1020 | } | |
1021 | ||
1022 | template<typename _Funct> | |
1023 | void | |
1024 | run_steps_to_limit(const _Funct& __f) | |
1025 | { | |
1026 | size_t i(1); | |
1027 | bool exit(false); | |
1028 | auto a = _M_container.get_allocator(); | |
1029 | ||
1030 | do | |
1031 | { | |
1032 | // Use the current step as an allocator label. | |
1033 | a.set_label(i); | |
1034 | ||
1035 | try | |
1036 | { | |
1037 | condition_type::limit_adjustor limit(i); | |
1038 | __f(_M_container); | |
1039 | ||
1040 | // If we get here, done. | |
1041 | exit = true; | |
1042 | } | |
1043 | catch(const __gnu_cxx::forced_error&) | |
1044 | { | |
1045 | // Check this step for allocations. | |
1046 | // NB: Will throw std::logic_error if allocations. | |
1047 | a.check_allocated(i); | |
1048 | ||
1049 | // Check memory allocated with operator new. | |
1050 | ||
1051 | ++i; | |
1052 | } | |
1053 | } | |
1054 | while (!exit); | |
1055 | ||
1056 | // Log count info. | |
1057 | std::cout << __f.target_type().name() << std::endl; | |
1058 | std::cout << "end count " << i << std::endl; | |
1059 | } | |
1060 | }; | |
1061 | ||
1062 | ||
1063 | // Run through all member functions with a no throw requirement, sudden death. | |
1064 | // all: member functions erase, pop_back, pop_front, swap | |
1065 | // iterator copy ctor, assignment operator | |
1066 | // unordered and associative: clear | |
1067 | // NB: Assumes _Tp::allocator_type is __gnu_cxx::throw_allocator_random. | |
1068 | template<typename _Tp> | |
1069 | struct generation_prohibited : public test_base<_Tp> | |
1070 | { | |
1071 | typedef _Tp container_type; | |
1072 | typedef test_base<container_type> base_type; | |
1073 | typedef typename base_type::populate populate; | |
1074 | typedef __gnu_cxx::random_condition condition_type; | |
1075 | ||
1076 | container_type _M_container; | |
1077 | ||
1078 | generation_prohibited() { run(); } | |
1079 | ||
1080 | void | |
1081 | run() | |
1082 | { | |
1083 | // Furthermore, assumes that the test functor will throw | |
1084 | // forced_exception via throw_allocator, that all errors are | |
1085 | // propagated and in error. Sudden death! | |
1086 | ||
1087 | // Setup. | |
1088 | { | |
1089 | condition_type::never_adjustor off; | |
1090 | populate p1(_M_container); | |
1091 | populate p2(base_type::_M_swap._M_other); | |
1092 | } | |
1093 | ||
1094 | // Run tests. | |
1095 | { | |
1096 | condition_type::always_adjustor on; | |
1097 | ||
714902c8 PC |
1098 | // NB: Vector and deque are special, erase can throw if the copy |
1099 | // constructor or assignment operator of value_type throws. | |
1100 | if (!traits<container_type>::has_throwing_erase::value) | |
1101 | { | |
1102 | _M_erasep(_M_container); | |
1103 | _M_eraser(_M_container); | |
1104 | } | |
53179144 BK |
1105 | |
1106 | _M_popf(_M_container); | |
1107 | _M_popb(_M_container); | |
1108 | ||
1109 | _M_iops(_M_container); | |
1110 | _M_ciops(_M_container); | |
1111 | ||
1112 | _M_swap(_M_container); | |
1113 | ||
1114 | // Last. | |
1115 | _M_clear(_M_container); | |
1116 | } | |
1117 | } | |
1118 | }; | |
1119 | ||
1120 | ||
1121 | // Test strong exception guarantee. | |
1122 | // Run through all member functions with a roll-back, consistent | |
1123 | // coherent requirement. | |
1124 | // all: member functions insert of a single element, push_back, push_front | |
1125 | // unordered: rehash | |
1126 | template<typename _Tp> | |
1127 | struct propagation_consistent : public test_base<_Tp> | |
1128 | { | |
1129 | typedef _Tp container_type; | |
1130 | typedef test_base<container_type> base_type; | |
1131 | typedef typename base_type::populate populate; | |
1132 | typedef std::function<void(container_type&)> function_type; | |
1133 | typedef __gnu_cxx::limit_condition condition_type; | |
1134 | ||
1135 | using base_type::compare; | |
1136 | ||
1137 | container_type _M_container_test; | |
1138 | container_type _M_container_control; | |
1139 | std::vector<function_type> _M_functions; | |
1140 | ||
1141 | propagation_consistent() { run(); } | |
1142 | ||
1143 | void | |
1144 | sync() | |
1145 | { _M_container_test = _M_container_control; } | |
1146 | ||
1147 | // Run test. | |
1148 | void | |
1149 | run() | |
1150 | { | |
1151 | // Setup. | |
1152 | condition_type::never_adjustor off; | |
1153 | ||
1154 | // Construct containers. | |
1155 | populate p(_M_container_control); | |
1156 | sync(); | |
1157 | ||
1158 | // Construct list of member functions to exercise. | |
1159 | _M_functions.push_back(function_type(base_type::_M_pushf)); | |
1160 | _M_functions.push_back(function_type(base_type::_M_pushb)); | |
1161 | _M_functions.push_back(function_type(base_type::_M_insertp)); | |
1162 | _M_functions.push_back(function_type(base_type::_M_rehash)); | |
1163 | ||
1164 | // Run tests. | |
53179144 BK |
1165 | for (auto i = _M_functions.begin(); i != _M_functions.end(); ++i) |
1166 | { | |
1167 | function_type& f = *i; | |
1168 | run_steps_to_limit(f); | |
1169 | } | |
1170 | } | |
1171 | ||
1172 | template<typename _Funct> | |
1173 | void | |
1174 | run_steps_to_limit(const _Funct& __f) | |
1175 | { | |
1176 | size_t i(1); | |
1177 | bool exit(false); | |
1178 | ||
1179 | do | |
1180 | { | |
1181 | sync(); | |
1182 | ||
1183 | try | |
1184 | { | |
1185 | condition_type::limit_adjustor limit(i); | |
1186 | __f(_M_container_test); | |
1187 | ||
1188 | // If we get here, done. | |
1189 | exit = true; | |
1190 | } | |
1191 | catch(const __gnu_cxx::forced_error&) | |
1192 | { | |
1193 | compare(_M_container_control, _M_container_test); | |
1194 | ++i; | |
1195 | } | |
1196 | } | |
1197 | while (!exit); | |
1198 | ||
1199 | // Log count info. | |
1200 | std::cout << __f.target_type().name() << std::endl; | |
1201 | std::cout << "end count " << i << std::endl; | |
1202 | } | |
1203 | }; | |
1204 | ||
1205 | } // namespace __gnu_test | |
1206 | ||
1207 | #endif |