Sequoia
Loading...
Searching...
No Matches
AllocationCheckersDetails.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2020. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
19
21{
23 {
24 [[nodiscard]]
25 std::string operator()(int count, int) const;
26 };
27
28 template<test_mode Mode, movable_comparable T, alloc_getter<T> Getter, auto Event>
29 bool check_allocation(std::string_view detail,
30 test_logger<Mode>& logger,
31 const T& container,
33 const int previous,
34 const alloc_prediction<Event> prediction)
35 {
36 const auto current{info.count(container)};
37 const auto unshifted{prediction.unshifted()};
38 const auto delta{prediction.value() - unshifted};
39
40 using allocator = decltype(std::declval<Getter>()(container));
41 const auto message{append_lines(make_type_info<allocator>(), detail)};
42
43 return check(equality, message, logger, current - previous - delta, unshifted, tutor{allocation_advice{}});
44 }
45
58 template<movable_comparable T, alloc_getter<T> Getter>
60 {
61 public:
62 using value_type = T;
64 using predictions_type = typename alloc_info::predictions_type;
65 using allocator_type = typename alloc_info::allocator_type;
66
67 dual_allocation_checker(alloc_info i, const T& x, const T& y)
68 : m_Info{std::move(i)}
69 , m_FirstCount{m_Info.count(x)}
70 , m_SecondCount{m_Info.count(y)}
71 , m_AllocatorsEqual{m_Info.allocator(x) == m_Info.allocator(y)}
72 {}
73
74 [[nodiscard]]
75 const alloc_info& info() const noexcept
76 {
77 return m_Info;
78 }
79
80 [[nodiscard]]
81 int first_count() const noexcept
82 {
83 return m_FirstCount;
84 }
85
86 [[nodiscard]]
87 int second_count() const noexcept
88 {
89 return m_SecondCount;
90 }
91
92 template<auto AllocEvent, test_mode Mode>
93 void check_no_allocation(std::string_view detail, test_logger<Mode>& logger, const T& x, const T& y) const
94 {
95 const alloc_prediction<AllocEvent> prediction{};
96 const auto xCount{allocation_count_shifter<T>::shift(first_count(), prediction)};
97 const auto yCount{second_count()};
98
99 check_allocation(append_lines(detail, "Unexpected allocation detected (x)"), logger, x, info(), xCount, prediction);
100 check_allocation(append_lines(detail, "Unexpected allocation detected (y)"), logger, y, info(), yCount, prediction);
101 }
102
103 template<test_mode Mode>
104 void check_copy_assign_y_to_x(test_logger<Mode>& logger, const T& x, const T& y) const
105 {
106 constexpr bool propagate{std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value};
107 if constexpr(propagate)
108 {
109 const auto xPrediction{info().get_predictions().assign_y_to_x().with_propagation};
110 const auto xCount{allocation_count_shifter<T>::shift(second_count(), xPrediction)};
111 check_allocation("Unexpected allocation detected for propagating copy assignment (x)", logger, x, info(), xCount, xPrediction);
112
113 const auto yPrediction{convert<null_allocation_event::spectator>(xPrediction)};
114 const auto yCount{allocation_count_shifter<T>::shift(second_count(), yPrediction)};
115 check_allocation("Unexpected allocation detected for propagating copy assignment (y)", logger, y, info(), yCount, yPrediction);
116 }
117 else
118 {
119 const auto xPrediction{info().get_predictions().assign_y_to_x().without_propagation};
120 const auto xCount{allocation_count_shifter<T>::shift(first_count(), xPrediction)};
121 check_allocation("Unexpected allocation detected for copy assignment (x)", logger, x, info(), xCount, xPrediction);
122
124 const auto yCount{allocation_count_shifter<T>::shift(second_count(), yPrediction)};
125 check_allocation("Unexpected allocation detected for copy assignment (y)", logger, y, info(), yCount, yPrediction);
126 }
127 }
128
129 template<test_mode Mode>
130 void check_move_assign_y_to_x(test_logger<Mode>& logger, const T& x) const
131 {
132 constexpr bool propagate{std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value};
133 const auto& predictions{info().get_predictions()};
134
135 if (m_AllocatorsEqual || propagate)
136 {
137 const auto xPrediction{predictions.move_assign_allocs()};
138 check_allocation("Unexpected allocation detected for propagating move assignment (x)", logger, x, info(), second_count(), xPrediction);
139 }
140 else
141 {
142 const auto xPrediction{predictions.move_assign_no_prop_allocs()};
143 check_allocation("Unexpected allocation detected for move assignment (x)", logger, x, info(), first_count(), xPrediction);
144 }
145 }
146
147 template<test_mode Mode>
148 void check_mutation_after_swap(test_logger<Mode>& logger, const T& lhs, const T& rhs) const
149 {
150 auto lhCount{first_count()}, rhCount{second_count()};
151
152 if constexpr(std::allocator_traits<allocator_type>::propagate_on_container_swap::value)
153 {
154 std::ranges::swap(lhCount, rhCount);
155 }
156
157 const auto lhPrediction{info().get_predictions().mutation_allocs()};
158 check_allocation("Unexpected allocation detected following mutation after swap (y)", logger, lhs, info(), lhCount, lhPrediction);
159
161 check_allocation("Unexpected allocation detected following mutation after swap (x)", logger, rhs, info(), rhCount, rhPrediction);
162 }
163 private:
164 alloc_info m_Info{};
165 int m_FirstCount{}, m_SecondCount{};
166 bool m_AllocatorsEqual{};
167 };
168
169 //================================ Deduction guide ================================//
170
171 template<movable_comparable T, alloc_getter<T> Getter>
174
185 template<movable_comparable T, alloc_getter<T> Getter>
187 {
188 public:
189 using value_type = T;
191 using predictions_type = typename alloc_info::predictions_type;
192 using allocator_type = typename alloc_info::allocator_type;
193
194 allocation_checker(alloc_info i, const int priorCount=0)
195 : m_Info{std::move(i)}
196 , m_PriorCount{priorCount}
197 {}
198
199 allocation_checker(alloc_info i, const T& x)
200 : m_Info{std::move(i)}
201 , m_PriorCount{m_Info.count(x)}
202 {}
203
204 template<test_mode Mode, auto Event>
205 bool check(std::string_view detail, test_logger<Mode>& logger, const T& container, const alloc_prediction<Event> prediction) const
206 {
207 const auto count{allocation_count_shifter<T>::shift(m_PriorCount, prediction)};
208 return check_allocation(detail, logger, container, info(), count, prediction);
209 }
210
211 [[nodiscard]]
212 const alloc_info& info() const noexcept
213 {
214 return m_Info;
215 }
216 private:
217 alloc_info m_Info{};
218 int m_PriorCount{};
219 };
220
221 //================================ Deduction guides ================================//
222
223 template<movable_comparable T, alloc_getter<T> Getter>
226
227 template<movable_comparable T, alloc_getter<T> Getter>
230
231 //================================ Specializations of do_swap ================================//
232
233 template<class T, alloc_getter<T>... Getters>
234 struct do_swap<dual_allocation_checker<T, Getters>...>
235 {
236 constexpr static bool value{
237 (( std::allocator_traits<typename dual_allocation_checker<T, Getters>::allocator_type>::propagate_on_container_swap::value
238 || std::allocator_traits<typename dual_allocation_checker<T, Getters>::allocator_type>::is_always_equal::value) && ...)
239 };
240 };
241
242 template<class T, alloc_getter<T>... Getters>
243 struct do_swap<allocation_checker<T, Getters>...>
244 {
245 constexpr static bool value{
246 (( std::allocator_traits<typename allocation_checker<T, Getters>::allocator_type>::propagate_on_container_swap::value
247 || std::allocator_traits<typename allocation_checker<T, Getters>::allocator_type>::is_always_equal::value) && ...)
248 };
249 };
250
251 //================================ make_scoped_allocation_checkers ================================//
252
253 template
254 <
255 template<class, class> class Checker,
257 alloc_getter<T> Getter,
258 class... Args,
259 std::size_t... I
260 >
261 [[nodiscard]]
262 auto make_scoped_allocation_checkers(const allocation_info<T, Getter>& info, std::index_sequence<I...>, Args&&... args)
263 {
264 return std::make_tuple(Checker{info.template unpack<I>(), std::forward<Args>(args)...}...);
265 }
266
267 template
268 <
269 template<class, class> class Checker,
271 alloc_getter<T> Getter,
272 class... Args
273 >
274 [[nodiscard]]
275 auto make_scoped_allocation_checkers(const allocation_info<T, Getter>& info, Args&&... args)
276 {
277 constexpr std::size_t N{allocation_info<T, Getter>::size};
278 return make_scoped_allocation_checkers<Checker>(info, std::make_index_sequence<N>{}, std::forward<Args>(args)...);
279 }
280
281 //================================ make_dual_allocation_checkers ================================//
282
283 template<movable_comparable T, alloc_getter<T> Getter, class... Args>
284 [[nodiscard]]
285 std::tuple<dual_allocation_checker<T, Getter>>
286 make_dual_allocation_checkers(const allocation_info<T, Getter>& info, Args&&... args)
287 {
288 using checker = dual_allocation_checker<T, Getter>;
289 return {checker{info, std::forward<Args>(args)...}};
290 }
291
292 template<movable_comparable T, alloc_getter<T> Getter, class... Args>
293 requires scoped_alloc<std::invoke_result_t<Getter, T>>
294 [[nodiscard]]
295 auto make_dual_allocation_checkers(const allocation_info<T, Getter>& info, Args&&... args)
296 {
297 return make_scoped_allocation_checkers<dual_allocation_checker>(info, std::forward<Args>(args)...);
298 }
299
300 //================================ make_allocation_checkers ================================//
301
302 template<movable_comparable T, alloc_getter<T> Getter, class... Args>
303 [[nodiscard]]
304 std::tuple<allocation_checker<T, Getter>>
305 make_allocation_checkers(const allocation_info<T, Getter>& info, Args&&... args)
306 {
307 using checker = allocation_checker<T, Getter>;
308 return {checker{info, std::forward<Args>(args)...}};
309 }
310
311 template<movable_comparable T, alloc_getter<T> Getter, class... Args>
312 requires scoped_alloc<std::invoke_result_t<Getter, T>>
313 [[nodiscard]]
314 auto make_allocation_checkers(const allocation_info<T, Getter>& info, Args&&... args)
315 {
316 return make_scoped_allocation_checkers<allocation_checker>(info, std::forward<Args>(args)...);
317 }
318
319 //================================ checks using dual_allocation_checker ================================//
320
321 template<auto AllocEvent, test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
322 requires (sizeof...(Getters) > 0)
323 void check_no_allocation(std::string_view detail, test_logger<Mode>& logger, const T& x, const T& y, const dual_allocation_checker<T, Getters>&... checkers)
324 {
325 auto checkFn{
326 [detail, &logger, &x, &y](const auto& checker){
327 checker.template check_no_allocation<AllocEvent>(detail, logger, x, y);
328 }
329 };
330
331 (checkFn(checkers), ...);
332 }
333
334 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
335 requires (sizeof...(Getters) > 0)
336 void check_copy_assign_allocation(test_logger<Mode>& logger, const T& x, const T& y, const dual_allocation_checker<T, Getters>&... checkers)
337 {
338 auto checkFn{
339 [&logger, &x, &y](const auto& checker){
340 checker.check_copy_assign_y_to_x(logger, x, y);
341 }
342 };
343
344 (checkFn(checkers), ...);
345 }
346
347 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
348 requires (sizeof...(Getters) > 0)
349 void check_move_assign_allocation(test_logger<Mode>& logger, const T& x, const dual_allocation_checker<T, Getters>&... checkers)
350 {
351 auto checkFn{
352 [&logger, &x](const auto& checker){
353 checker.check_move_assign_y_to_x(logger, x);
354 }
355 };
356
357 (checkFn(checkers), ...);
358 }
359
360 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
361 requires (sizeof...(Getters) > 0)
362 void check_mutation_after_swap(test_logger<Mode>& logger, const T& x, const T& y, const dual_allocation_checker<T, Getters>&... checkers)
363 {
364 auto checkFn{
365 [&logger, &x, &y](const auto& checker){
366 checker.check_mutation_after_swap(logger, x, y);
367 }
368 };
369
370 (checkFn(checkers), ...);
371 }
372
373 template<test_mode Mode, movable_comparable T, std::invocable<T&> Mutator, alloc_getter<T>... Getters>
374 requires (sizeof...(Getters) > 0)
375 void check_mutation_after_swap(test_logger<Mode>& logger, T& lhs, const T& rhs, const T& y, Mutator yMutator, dual_allocation_checker<T, Getters>... checkers)
376 {
377 if(check("Mutation after swap pre-condition violated", logger, lhs == y))
378 {
379 yMutator(lhs);
380 check_mutation_after_swap(logger, lhs, rhs, checkers...);
381
382 check("Mutation is not doing anything following copy then swap", logger, lhs != y);
383 }
384 }
385
386 //================================ checks using allocation_checker ================================//
387
388 template<auto AllocEvent, test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
389 requires (sizeof...(Getters) > 0)
390 void check_no_allocation(test_logger<Mode>& logger, const T& container, std::string_view tag, const allocation_checker<T, Getters>&... checkers)
391 {
392 auto checkFn{
393 [tag, &logger, &container](const auto& checker){
394 const alloc_prediction<AllocEvent> prediction{};
395 checker.check(std::string{"Unexpected allocation detected "}.append(tag), logger, container, prediction);
396 }
397 };
398
399 (checkFn(checkers), ...);
400 }
401
402 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
403 requires (sizeof...(Getters) > 0)
404 void check_copy_x_allocation(test_logger<Mode>& logger, const T& container, const allocation_checker<T, Getters>&... checkers)
405 {
406 auto checkFn{
407 [&logger, &container](const auto& checker){
408 const auto prediction{checker.info().get_predictions().x()};
409 checker.check("Unexpected allocation detected for copy construction (x)", logger, container, prediction);
410 }
411 };
412
413 (checkFn(checkers), ...);
414 }
415
416 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
417 requires (sizeof...(Getters) > 0)
418 void check_copy_y_allocation(test_logger<Mode>& logger, const T& container, const allocation_checker<T, Getters>&... checkers)
419 {
420 auto checkFn{
421 [&logger, &container](const auto& checker){
422 const auto prediction{checker.info().get_predictions().y().copy};
423 checker.check("Unexpected allocation detected for copy construction (y)", logger, container, prediction);
424 }
425 };
426
427 (checkFn(checkers), ...);
428 }
429
430 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
431 requires (sizeof...(Getters) > 0)
432 void check_move_y_allocation(test_logger<Mode>& logger, const T& container, const allocation_checker<T, Getters>&... checkers)
433 {
434 auto checkFn{
435 [&logger, &container](const auto& checker){
436 const auto prediction{checker.info().get_predictions().move_allocs()};
437 checker.check("Unexpected allocation detected for move construction (y)", logger, container, prediction);
438 }
439 };
440
441 (checkFn(checkers), ...);
442 }
443
444 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
445 requires (sizeof...(Getters) > 0)
446 void check_mutation_allocation(std::string_view priorOp, test_logger<Mode>& logger, const T& y, const allocation_checker<T, Getters>&... checkers)
447 {
448 auto checkFn{
449 [priorOp, &logger, &y](const auto& checker){
450 const auto prediction{checker.info().get_predictions().mutation_allocs()};
451 const auto mess{
452 std::string{"Unexpected allocation detected following mutation after "}.append(priorOp)
453 };
454
455 checker.check(mess, logger, y, prediction);
456 }
457 };
458
459 (checkFn(checkers), ...);
460 }
461
462 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
463 requires (sizeof...(Getters) > 0)
464 void check_para_copy_y_allocation(test_logger<Mode>& logger, const T& container, const allocation_checker<T, Getters>&... checkers)
465 {
466 auto checkFn{
467 [&logger, &container](const auto& checker){
468 const auto prediction{checker.info().get_predictions().y().para_copy};
469 checker.check("Unexpected allocation detected for para-copy construction (y)", logger, container, prediction);
470 }
471 };
472
473 (checkFn(checkers), ...);
474 }
475
476 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
477 requires (sizeof...(Getters) > 0)
478 void check_para_copy_y_allocation(test_logger<Mode>& logger, const T& container, std::tuple<allocation_checker<T, Getters>...> checkers)
479 {
480 auto fn{[&logger,&container](auto&&... checkers){
481 check_para_copy_y_allocation(logger, container, std::forward<decltype(checkers)>(checkers)...);
482 }
483 };
484
485 std::apply(fn, checkers);
486 }
487
488 template<test_mode Mode, container_tag tag, movable_comparable T, alloc_getter<T>... Getters>
489 requires (sizeof...(Getters) > 0)
490 void check_para_move_allocation(test_logger<Mode>& logger,
491 container_tag_constant<tag>,
492 const T& container,
493 const allocation_checker<T, Getters>&... checkers)
494 {
495 auto checkFn{
496 [&logger, &container](const auto& checker){
497 const auto prediction{
498 [&checker](){
499 if constexpr(container_tag_constant<tag>::value == container_tag::x)
500 {
501 return checker.info().get_predictions().para_move_x_allocs();
502 }
503 else
504 {
505 return checker.info().get_predictions().para_move_y_allocs();
506 }
507 }()
508 };
509
510 checker.check("Unexpected allocation detected for para-move construction (y)", logger, container, prediction);
511 }
512 };
513
514 (checkFn(checkers), ...);
515 }
516
517 template<test_mode Mode, container_tag tag, movable_comparable T, alloc_getter<T>... Getters>
518 void check_para_move_allocation(test_logger<Mode>& logger,
519 container_tag_constant<tag>,
520 const T& container,
521 std::tuple<allocation_checker<T, Getters>...> checkers)
522 {
523 auto fn{
524 [&logger, &container](auto&&... checkers){
525 using ctag = container_tag_constant<tag>;
526 check_para_move_allocation(logger, ctag{}, container, std::forward<decltype(checkers)>(checkers)...);
527 }
528 };
529
530 std::apply(fn, checkers);
531 }
532
533 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
534 requires (sizeof...(Getters) > 0)
535 void check_initialization_allocations(test_logger<Mode>& logger, const T& x, const T& y, const allocation_info<T, Getters>&... info)
536 {
537 check_init_allocations(logger, x, y, std::tuple_cat(make_allocation_checkers(info, 0)...));
538 }
539
540 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
541 requires (sizeof...(Getters) > 0)
542 void check_init_allocations(test_logger<Mode>& logger, const T& x, const T& y, const allocation_checker<T, Getters>&... checkers)
543 {
544 auto checkFn{
545 [&logger, &x, &y](const auto& checker){
546 {
547 const auto prediction{checker.info().get_predictions().x()};
548 checker.check("Unexpected allocation detected for initialization (x)", logger, x, prediction);
549 }
550
551 {
552 const auto prediction{
553 [&checker](){
554 if constexpr(pseudoregular<T>)
555 {
556 return checker.info().get_predictions().y().copy;
557 }
558 else
559 {
560 return checker.info().get_predictions().y().para_move;
561 }
562 }()
563 };
564
565 checker.check("Unexpected allocation detected for initialization (y)", logger, y, prediction);
566 }
567 }
568 };
569
570 (checkFn(checkers), ...);
571 }
572
573 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
574 void check_init_allocations(test_logger<Mode>& logger, const T& x, const T& y, std::tuple<allocation_checker<T, Getters>...> checkers)
575 {
576 auto fn{
577 [&logger, &x, &y](auto&&... checkers){
578 check_init_allocations(logger, x, y, std::forward<decltype(checkers)>(checkers)...);
579 }
580 };
581
582 std::apply(fn, checkers);
583 }
584
585 template<test_mode Mode, movable_comparable T, alloc_getter<T>... Getters>
586 requires (sizeof...(Getters) > 0)
587 void check_serialization_allocation(test_logger<Mode>& logger, const T& container, const allocation_checker<T, Getters>&... checkers)
588 {
589 auto checkFn{
590 [&logger, &container](const auto& checker){
591 const alloc_prediction<null_allocation_event::serialization> prediction{};
592 checker.check("Unexpected allocation detected for serialization (y)", logger, container, prediction);
593 }
594 };
595
596 (checkFn(checkers), ...);
597 }
598
599
601 template<movable_comparable T>
603 {
604 private:
605 template<class... Checkers>
606 constexpr static bool do_check_swap()
607 {
608 return (( std::allocator_traits<typename Checkers::allocator_type>::propagate_on_container_move_assignment::value
609 && std::allocator_traits<typename Checkers::allocator_type>::propagate_on_container_swap::value) && ... );
610 }
611
612 public:
613 using auxiliary_data_policy<T>::auxiliary_data_policy;
614
615 template<test_mode Mode, comparison_flavour C, alloc_getter<T>... Getters>
616 static bool post_comparison_action(test_logger<Mode>& logger, comparison_constant<C> comparison, const T& x, std::string_view tag, const allocation_checker<T, Getters>&... checkers)
617 {
618 sentinel<Mode> s{logger, ""};
619
620 const auto mess{std::string{"for operator"}.append(to_string(comparison.value)).append(" ").append(tag)};
621 check_no_allocation<C>(logger, x, mess, checkers...);
622
623 return !s.failure_detected();
624 }
625
626 template<test_mode Mode, alloc_getter<T>... Getters>
627 static void post_move_action(test_logger<Mode>& logger, const T& y, const allocation_checker<T, Getters>&... checkers)
628 {
629 check_move_y_allocation(logger, y, checkers...);
630 }
631
632 template<test_mode Mode, std::invocable<T&> Mutator, alloc_getter<T>... Getters>
633 static void post_move_assign_action(test_logger<Mode>& logger, T& y, Mutator yMutator, const dual_allocation_checker<T, Getters>&... checkers)
634 {
635 check_move_assign_allocation(logger, y, checkers...);
636 check_mutation_after_move("assignment", logger, y, std::move(yMutator), allocation_checker{checkers.info(), y}...);
637 }
638
639 template<test_mode Mode, class U, alloc_getter<T>... Getters>
640 static void post_swap_action([[maybe_unused]] test_logger<Mode>& logger,
641 [[maybe_unused]] const T& x,
642 [[maybe_unused]] const T& y,
643 const U&,
644 [[maybe_unused]] const dual_allocation_checker<T, Getters>&... checkers)
645 {
646 if constexpr (do_check_swap<dual_allocation_checker<T, Getters>...>())
647 {
648 check_no_allocation<null_allocation_event::swap>("Unexpected allocation detected for swap", logger, y, x, checkers...);
649 }
650 }
651
652 template<test_mode Mode, alloc_getter<T>... Getters>
653 static void post_serialization_action(test_logger<Mode>& logger, const T& y, const allocation_checker<T, Getters>&... checkers)
654 {
655 check_serialization_allocation(logger, y, checkers...);
656 }
657 };
658
666 template
667 <
668 test_mode Mode,
669 comparison_flavour C,
670 class Actions,
673 class... Getters
674 >
675 bool check_comparison_consistency(test_logger<Mode>& logger,
676 comparison_constant<C> comparison,
677 const Actions& actions,
678 const T& x,
679 const T& y,
680 Fn fn,
681 const dual_allocation_checker<T, Getters>&... checkers)
682 {
683 sentinel sentry{logger, ""};
684
685 do_check_comparison_consistency(logger, comparison, actions, x, "(x)", fn, allocation_checker(checkers.info(), x)...);
686 do_check_comparison_consistency(logger, comparison, actions, y, "(y)", fn, allocation_checker(checkers.info(), y)...);
687
688 return !sentry.failure_detected();
689 }
690
691
692 template<test_mode Mode, class Actions, movable_comparable T, class U, alloc_getter<T>... Getters>
693 requires checkable_against_for_semantics<Mode, T, U> && (sizeof...(Getters) > 0)
694 std::optional<T> check_move_construction(test_logger<Mode>& logger, const Actions& actions, T&& z, const U& y, optional_ref<const U> movedFrom, const dual_allocation_checker<T, Getters>&... checkers)
695 {
696 return do_check_move_construction(logger, actions, std::forward<T>(z), y, movedFrom, allocation_checker{checkers.info(), z}...);
697 }
698
699 template<test_mode Mode, class Actions, movable_comparable T, class U, std::invocable<T&> Mutator, alloc_getter<T>... Getters>
700 requires checkable_against_for_semantics<Mode, T, U> && (sizeof...(Getters) > 0)
701 void check_move_assign(test_logger<Mode>& logger, const Actions& actions, T& u, T&& v, const U& y, optional_ref<const U> movedFrom, Mutator yMutator, const dual_allocation_checker<T, Getters>&... checkers)
702 {
703 do_check_move_assign(logger, actions, u, std::forward<T>(v), y, movedFrom, std::move(yMutator), dual_allocation_checker{checkers.info(), u, v}...);
704 }
705
706 template<test_mode Mode, movable_comparable T, std::invocable<T&> Mutator, class... Checkers>
707 void check_mutation_after_move(std::string_view moveType, test_logger<Mode>& logger, T& t, Mutator yMutator, Checkers... checkers)
708 {
709 yMutator(t);
710 check_mutation_allocation(moveType, logger, t, checkers...);
711 }
712
713 template<test_mode Mode, movable_comparable T, std::invocable<T&> Mutator, class... Checkers, std::size_t... I>
714 void check_mutation_after_move(std::string_view moveType, test_logger<Mode>& logger, T& t, Mutator yMutator, std::tuple<Checkers...> checkers, std::index_sequence<I...>)
715 {
716 check_mutation_after_move(moveType, logger, t, std::move(yMutator), std::get<I>(checkers)...);
717 }
718
719 template<test_mode Mode, movable_comparable T, std::invocable<T&> Mutator, class... Checkers>
720 void check_mutation_after_move(std::string_view moveType, test_logger<Mode>& logger, T& t, Mutator yMutator, std::tuple<Checkers...> checkers)
721 {
722 check_mutation_after_move(moveType, logger, t, std::move(yMutator), std::move(checkers), std::make_index_sequence<sizeof...(Checkers)>{});
723 }
724
725 template<test_mode Mode, class Actions, movable_comparable T, alloc_getter<T>... Getters>
726 bool check_serialization(test_logger<Mode>& logger, const Actions& actions, T&& u, const T& y, const dual_allocation_checker<T, Getters>&... checkers)
727 {
728 return do_check_serialization(logger, actions, std::forward<T>(u), y, allocation_checker{checkers.info(), y}...);
729 }
730}
Core components for the Allocation Testing framework.
Free functions for performing checks, together with the 'checker' class template which wraps them.
bool check(CheckType flavour, std::string description, test_logger< Mode > &logger, Iter first, Sentinel last, PredictionIter predictionFirst, PredictionSentinel predictionLast, tutor< Advisor > advisor={})
The workhorse for comparing the contents of ranges.
Definition: FreeCheckers.hpp:377
Implementation details for semantics checks that cleanly supports types which do/do not have allocato...
Definition: AllocationCheckersCore.hpp:73
A specialization of allocation_info appropriate for std::scoped_allocator_adaptor.
Definition: AllocationCheckers.hpp:503
Class for use with a container possessing a (shared counting) allocator.
Definition: AllocationCheckers.hpp:425
Wraps allocation_info, together with the prior allocation count.
Definition: AllocationCheckersDetails.hpp:187
Wraps allocation_info, together with two prior allocation counts.
Definition: AllocationCheckersDetails.hpp:60
Definition: TestLogger.hpp:277
Definition: TestLogger.hpp:183
Supplements std::invocable.
Definition: Concepts.hpp:24
Building block for concepts related to std::regular but without the requirement of default constructi...
Definition: Concepts.hpp:35
Definition: AllocationCheckersTraits.hpp:24
Condition for applying a container check.
Definition: AllocationCheckersDetails.cpp:15
Definition: PhysicalValuesDetails.hpp:258
Definition: AllocationCheckers.hpp:200
actions common to both move-only and regular types.
Definition: AllocationCheckersDetails.hpp:603
Definition: AllocationCheckersDetails.hpp:23
Definition: SemanticsCheckersDetails.hpp:121
Definition: SemanticsCheckersDetails.hpp:108