25 std::string operator()(
int count,
int)
const;
28 template<test_mode Mode, movable_comparable T, alloc_getter<T> Getter, auto Event>
29 bool check_allocation(std::string_view detail,
36 const auto current{info.count(container)};
37 const auto unshifted{prediction.unshifted()};
38 const auto delta{prediction.value() - unshifted};
40 using allocator =
decltype(std::declval<Getter>()(container));
41 const auto message{append_lines(make_type_info<allocator>(), detail)};
43 return check(equality, message, logger, current - previous - delta, unshifted, tutor{allocation_advice{}});
58 template<movable_comparable T, alloc_getter<T> Getter>
64 using predictions_type =
typename alloc_info::predictions_type;
65 using allocator_type =
typename alloc_info::allocator_type;
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)}
81 int first_count()
const noexcept
87 int second_count()
const noexcept
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
97 const auto yCount{second_count()};
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);
103 template<test_mode Mode>
104 void check_copy_assign_y_to_x(
test_logger<Mode>& logger,
const T& x,
const T& y)
const
106 constexpr bool propagate{std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value};
107 if constexpr(propagate)
109 const auto xPrediction{info().get_predictions().assign_y_to_x().with_propagation};
111 check_allocation(
"Unexpected allocation detected for propagating copy assignment (x)", logger, x, info(), xCount, xPrediction);
113 const auto yPrediction{convert<null_allocation_event::spectator>(xPrediction)};
115 check_allocation(
"Unexpected allocation detected for propagating copy assignment (y)", logger, y, info(), yCount, yPrediction);
119 const auto xPrediction{info().get_predictions().assign_y_to_x().without_propagation};
121 check_allocation(
"Unexpected allocation detected for copy assignment (x)", logger, x, info(), xCount, xPrediction);
125 check_allocation(
"Unexpected allocation detected for copy assignment (y)", logger, y, info(), yCount, yPrediction);
129 template<test_mode Mode>
132 constexpr bool propagate{std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value};
133 const auto& predictions{info().get_predictions()};
135 if (m_AllocatorsEqual || propagate)
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);
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);
147 template<test_mode Mode>
148 void check_mutation_after_swap(
test_logger<Mode>& logger,
const T& lhs,
const T& rhs)
const
150 auto lhCount{first_count()}, rhCount{second_count()};
152 if constexpr(std::allocator_traits<allocator_type>::propagate_on_container_swap::value)
154 std::ranges::swap(lhCount, rhCount);
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);
161 check_allocation(
"Unexpected allocation detected following mutation after swap (x)", logger, rhs, info(), rhCount, rhPrediction);
165 int m_FirstCount{}, m_SecondCount{};
166 bool m_AllocatorsEqual{};
171 template<movable_comparable T, alloc_getter<T> Getter>
185 template<movable_comparable T, alloc_getter<T> Getter>
189 using value_type = T;
191 using predictions_type =
typename alloc_info::predictions_type;
192 using allocator_type =
typename alloc_info::allocator_type;
195 : m_Info{std::move(i)}
196 , m_PriorCount{priorCount}
200 : m_Info{std::move(i)}
201 , m_PriorCount{m_Info.count(x)}
204 template<test_mode Mode, auto Event>
208 return check_allocation(detail, logger, container, info(), count, prediction);
223 template<movable_comparable T, alloc_getter<T> Getter>
227 template<movable_comparable T, alloc_getter<T> Getter>
233 template<
class T, alloc_getter<T>... Getters>
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) && ...)
242 template<
class T, alloc_getter<T>... Getters>
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) && ...)
255 template<
class,
class>
class Checker,
264 return std::make_tuple(Checker{info.template
unpack<I>(), std::forward<Args>(args)...}...);
269 template<
class,
class>
class Checker,
271 alloc_getter<T> Getter,
275 auto make_scoped_allocation_checkers(
const allocation_info<T, Getter>& info, Args&&... args)
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)...);
283 template<movable_comparable T, alloc_getter<T> Getter,
class... Args>
285 std::tuple<dual_allocation_checker<T, Getter>>
286 make_dual_allocation_checkers(
const allocation_info<T, Getter>& info, Args&&... args)
288 using checker = dual_allocation_checker<T, Getter>;
289 return {checker{info, std::forward<Args>(args)...}};
292 template<movable_comparable T, alloc_getter<T> Getter,
class... Args>
293 requires scoped_alloc<std::invoke_result_t<Getter, T>>
295 auto make_dual_allocation_checkers(
const allocation_info<T, Getter>& info, Args&&... args)
297 return make_scoped_allocation_checkers<dual_allocation_checker>(info, std::forward<Args>(args)...);
302 template<movable_comparable T, alloc_getter<T> Getter,
class... Args>
304 std::tuple<allocation_checker<T, Getter>>
305 make_allocation_checkers(
const allocation_info<T, Getter>& info, Args&&... args)
307 using checker = allocation_checker<T, Getter>;
308 return {checker{info, std::forward<Args>(args)...}};
311 template<movable_comparable T, alloc_getter<T> Getter,
class... Args>
312 requires scoped_alloc<std::invoke_result_t<Getter, T>>
314 auto make_allocation_checkers(
const allocation_info<T, Getter>& info, Args&&... args)
316 return make_scoped_allocation_checkers<allocation_checker>(info, std::forward<Args>(args)...);
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)
326 [detail, &logger, &x, &y](
const auto& checker){
327 checker.template check_no_allocation<AllocEvent>(detail, logger, x, y);
331 (checkFn(checkers), ...);
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)
339 [&logger, &x, &y](
const auto& checker){
340 checker.check_copy_assign_y_to_x(logger, x, y);
344 (checkFn(checkers), ...);
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)
352 [&logger, &x](
const auto& checker){
353 checker.check_move_assign_y_to_x(logger, x);
357 (checkFn(checkers), ...);
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)
365 [&logger, &x, &y](
const auto& checker){
366 checker.check_mutation_after_swap(logger, x, y);
370 (checkFn(checkers), ...);
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)
377 if(
check(
"Mutation after swap pre-condition violated", logger, lhs == y))
380 check_mutation_after_swap(logger, lhs, rhs, checkers...);
382 check(
"Mutation is not doing anything following copy then swap", logger, lhs != y);
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)
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);
399 (checkFn(checkers), ...);
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)
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);
413 (checkFn(checkers), ...);
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)
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);
427 (checkFn(checkers), ...);
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)
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);
441 (checkFn(checkers), ...);
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)
449 [priorOp, &logger, &y](
const auto& checker){
450 const auto prediction{checker.info().get_predictions().mutation_allocs()};
452 std::string{
"Unexpected allocation detected following mutation after "}.append(priorOp)
455 checker.check(mess, logger, y, prediction);
459 (checkFn(checkers), ...);
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)
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);
473 (checkFn(checkers), ...);
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)
480 auto fn{[&logger,&container](
auto&&... checkers){
481 check_para_copy_y_allocation(logger, container, std::forward<
decltype(checkers)>(checkers)...);
485 std::apply(fn, checkers);
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>,
493 const allocation_checker<T, Getters>&... checkers)
496 [&logger, &container](
const auto& checker){
497 const auto prediction{
499 if constexpr(container_tag_constant<tag>::value == container_tag::x)
501 return checker.info().get_predictions().para_move_x_allocs();
505 return checker.info().get_predictions().para_move_y_allocs();
510 checker.check(
"Unexpected allocation detected for para-move construction (y)", logger, container, prediction);
514 (checkFn(checkers), ...);
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>,
521 std::tuple<allocation_checker<T, Getters>...> checkers)
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)...);
530 std::apply(fn, checkers);
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)
537 check_init_allocations(logger, x, y, std::tuple_cat(make_allocation_checkers(info, 0)...));
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)
545 [&logger, &x, &y](
const auto& checker){
547 const auto prediction{checker.info().get_predictions().x()};
548 checker.check(
"Unexpected allocation detected for initialization (x)", logger, x, prediction);
552 const auto prediction{
554 if constexpr(pseudoregular<T>)
556 return checker.info().get_predictions().y().copy;
560 return checker.info().get_predictions().y().para_move;
565 checker.check(
"Unexpected allocation detected for initialization (y)", logger, y, prediction);
570 (checkFn(checkers), ...);
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)
577 [&logger, &x, &y](
auto&&... checkers){
578 check_init_allocations(logger, x, y, std::forward<
decltype(checkers)>(checkers)...);
582 std::apply(fn, checkers);
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)
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);
596 (checkFn(checkers), ...);
601 template<movable_comparable T>
605 template<
class... Checkers>
606 constexpr static bool do_check_swap()
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) && ... );
615 template<test_mode Mode, comparison_flavour C, alloc_getter<T>... Getters>
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...);
623 return !s.failure_detected();
626 template<test_mode Mode, alloc_getter<T>... Getters>
629 check_move_y_allocation(logger, y, checkers...);
632 template<test_mode Mode, std::invocable<T&> Mutator, alloc_getter<T>... Getters>
635 check_move_assign_allocation(logger, y, checkers...);
636 check_mutation_after_move(
"assignment", logger, y, std::move(yMutator),
allocation_checker{checkers.info(), y}...);
639 template<test_mode Mode,
class U, alloc_getter<T>... Getters>
641 [[maybe_unused]]
const T& x,
642 [[maybe_unused]]
const T& y,
646 if constexpr (do_check_swap<dual_allocation_checker<T, Getters>...>())
648 check_no_allocation<null_allocation_event::swap>(
"Unexpected allocation detected for swap", logger, y, x, checkers...);
652 template<test_mode Mode, alloc_getter<T>... Getters>
655 check_serialization_allocation(logger, y, checkers...);
669 comparison_flavour C,
676 comparison_constant<C> comparison,
677 const Actions& actions,
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)...);
688 return !sentry.failure_detected();
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)
696 return do_check_move_construction(logger, actions, std::forward<T>(z), y, movedFrom, allocation_checker{checkers.info(), z}...);
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)
703 do_check_move_assign(logger, actions, u, std::forward<T>(v), y, movedFrom, std::move(yMutator), dual_allocation_checker{checkers.info(), u, v}...);
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)
710 check_mutation_allocation(moveType, logger, t, checkers...);
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...>)
716 check_mutation_after_move(moveType, logger, t, std::move(yMutator), std::get<I>(checkers)...);
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)
722 check_mutation_after_move(moveType, logger, t, std::move(yMutator), std::move(checkers), std::make_index_sequence<
sizeof...(Checkers)>{});
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)
728 return do_check_serialization(logger, actions, std::forward<T>(u), y, allocation_checker{checkers.info(), y}...);
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