22namespace sequoia::testing
24 enum class construction_allocation_event {
32 enum class assignment_allocation_event {
39 enum class individual_allocation_event {
43 template<auto To, auto From>
45 constexpr alloc_prediction<To> convert(alloc_prediction<From> p)
noexcept
47 return {p.unshifted(), p.value() - p.unshifted()};
50 using initialization_prediction = alloc_prediction<construction_allocation_event::initialization>;
51 using copy_prediction = alloc_prediction<construction_allocation_event::copy>;
52 using move_prediction = alloc_prediction<construction_allocation_event::move>;
53 using para_copy_prediction = alloc_prediction<construction_allocation_event::para_copy>;
54 using para_move_prediction = alloc_prediction<construction_allocation_event::para_move>;
55 using assign_prediction = alloc_prediction<assignment_allocation_event::assign>;
56 using assign_no_prop_prediction = alloc_prediction<assignment_allocation_event::assign_no_prop>;
57 using move_assign_prediction = alloc_prediction<assignment_allocation_event::move_assign>;
58 using move_assign_no_prop_prediction = alloc_prediction<assignment_allocation_event::move_assign_no_prop>;
59 using mutation_prediction = alloc_prediction<individual_allocation_event::mutation>;
62 consteval initialization_prediction
operator ""_init(
unsigned long long int n)
noexcept
64 return initialization_prediction{
static_cast<int>(n)};
68 consteval copy_prediction
operator ""_c(
unsigned long long int n)
noexcept
70 return copy_prediction{
static_cast<int>(n) };
74 consteval mutation_prediction
operator ""_mu(
unsigned long long int n)
noexcept
76 return mutation_prediction{
static_cast<int>(n) };
80 consteval para_copy_prediction
operator ""_pc(
unsigned long long int n)
noexcept
82 return para_copy_prediction{
static_cast<int>(n) };
86 consteval para_move_prediction
operator ""_pm(
unsigned long long int n)
noexcept
88 return para_move_prediction{
static_cast<int>(n) };
92 consteval assign_no_prop_prediction
operator ""_anp(
unsigned long long int n)
noexcept
94 return assign_no_prop_prediction{
static_cast<int>(n) };
98 consteval assign_prediction
operator ""_awp(
unsigned long long int n)
noexcept
100 return assign_prediction{
static_cast<int>(n) };
104 consteval move_assign_prediction
operator ""_ma(
unsigned long long int n)
noexcept
106 return move_assign_prediction{
static_cast<int>(n)};
110 consteval move_assign_no_prop_prediction
operator ""_manp(
unsigned long long int n)
noexcept
112 return move_assign_no_prop_prediction{
static_cast<int>(n) };
115 enum class scoped_prediction_corrections { num_containers, post_mutation };
117 template<scoped_prediction_corrections CorrectionFlavour>
121 constexpr explicit corrections(std::size_t n) : m_Num{n} {}
124 constexpr int value()
const noexcept
126 return static_cast<int>(m_Num);
146 consteval post_mutation_correction
operator ""_postmutation(
unsigned long long int n)
noexcept
148 return post_mutation_correction{
static_cast<std::size_t
>(n)};
156 , mutation_correction{postMutation}
163 namespace allocation_equivalence_classes
174 template<
class Allocator>
177 template<
class Allocator>
183 template<auto AllocEvent>
187 if constexpr (with_msvc_v && (iterator_debug_level() > 0))
189 const int unshifted{p.unshifted()};
190 return alloc_prediction<AllocEvent>{unshifted, val};
201 template<auto NullAllocEvent>
209 enum class top_level { no, yes };
219 template<
class Allocator>
231 const auto& c{tag == container_tag::x ? m_Counts.num_x : m_Counts.num_y};
232 return increment_msvc_debug_count(p, c.value());
238 return increment_msvc_debug_count(p, is_top_level() ? 1 : 0);
244 return increment_msvc_debug_count(p, m_Counts.num_y.value());
250 const auto& c{tag == container_tag::x ? m_Counts.num_x : m_Counts.num_y};
251 return increment_msvc_debug_count(p, c.value());
257 return increment_msvc_debug_count(p, m_Counts.mutation_correction.value());
263 return increment_msvc_debug_count(p, m_Counts.num_y.value());
269 return increment_msvc_debug_count(p, is_top_level() ? 1 : 0);
275 return increment_msvc_debug_count(p, is_top_level() ? 0 : m_Counts.num_y.value());
281 const bool doIncrement{!is_top_level() && (m_Counts.num_y.value() > m_Counts.num_x.value())};
282 return increment_msvc_debug_count(p, doIncrement ? m_Counts.num_y.value() : 0);
292 constexpr bool is_top_level()
const noexcept
294 return m_TopLevel == top_level::yes;
298 top_level m_TopLevel;
301 template<
class Allocator>
314 constexpr bool copyProp{std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value};
315 constexpr bool moveProp{std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value};
316 constexpr bool swapProp{std::allocator_traits<Allocator>::propagate_on_container_swap::value};
317 return !copyProp ? 1 :
323 return increment_msvc_debug_count(p, this->counts().num_y.value()*val);
329 return increment_msvc_debug_count(p, this->counts().num_y.value());
333 template<movable_comparable T, alloc_getter<T> Getter>
336 using allocator = std::remove_cvref_t<std::invoke_result_t<Getter, T>>;
340 template<movable_comparable T, alloc_getter<T> Getter>
341 requires requires {
typename Getter::alloc_equivalence_class; }
344 using type =
typename Getter::alloc_equivalence_class;
347 template<movable_comparable T, alloc_getter<T> Getter>
369 template<movable_comparable T, alloc_getter<T> Getter>
373 using allocator_type = std::invoke_result_t<Getter, T>;
376 : m_AllocatorGetter{std::move(allocGetter)}
382 int count(
const T& c)
const noexcept
384 return m_AllocatorGetter(c).allocs();
387 template<
class... Args>
389 allocator_type make_allocator(Args&&... args)
const
391 return {std::forward<Args>(args)...};
395 allocator_type allocator(
const T& c)
const
397 return m_AllocatorGetter(c);
408 constexpr Getter make_getter()
const
410 return m_AllocatorGetter;
414 Getter m_AllocatorGetter;
423 template<movable_comparable T, alloc_getter<T> Getter>
429 using value_type = T;
430 using allocator_type =
typename base_t::allocator_type;
431 using predictions_type = type_to_allocation_predictions_t<T>;
432 using inner_predictions_type = type_to_inner_allocation_predictions_t<T>;
434 constexpr allocation_info(Getter allocGetter,
const predictions_type& predictions)
435 :
base_t{std::move(allocGetter)}
436 , m_Predictions{prediction_shifter{}(predictions)}
439 constexpr allocation_info(Getter allocGetter,
const inner_predictions_type& predictions)
440 :
base_t{std::move(allocGetter)}
441 , m_Predictions{prediction_shifter{}(predictions)}
445 constexpr const predictions_type& get_predictions()
const noexcept
447 return m_Predictions;
450 predictions_type m_Predictions;
452 struct prediction_shifter
454 using allocClass = alloc_equivalence_class_generator_t<T, Getter>;
457 constexpr predictions_type operator()(
const predictions_type& predictions)
const
459 return shift<allocClass>(predictions);
463 constexpr predictions_type operator()(
const inner_predictions_type& predictions)
const
465 return to_top_level(shift<allocClass>(predictions));
473 class T = std::remove_cvref_t<
typename function_signature<
decltype(&std::remove_cvref_t<Fn>::operator())>::arg>
478 template<movable_comparable T, alloc_getter<T> Getter>
481 using alloc_equivalence_class = alloc_equivalence_class_generator_t<T, Getter>;
486 auto operator()(
const T& c)
const
488 return getter(c).outer_allocator();
499 template<movable_comparable T, alloc_getter<T> Getter>
507 using value_type = T;
508 using allocator_type =
typename base_t::allocator_type;
509 using predictions_type = type_to_allocation_predictions_t<T>;
510 using inner_predictions_type = type_to_inner_allocation_predictions_t<T>;
513 static_assert(size > 0);
516 predictions_type predictions,
517 std::initializer_list<inner_predictions_type> innerPredictions)
518 :
base_t{std::move(allocGetter)}
519 , m_Predictions{predictions}
520 , m_InnerPredictions{utilities::to_array<inner_predictions_type, size-1>(innerPredictions)}
525 template<std::
size_t I>
536 auto scopedGetter{[getter{this->make_getter()}] (
const T& c){
537 return get<I>(getter(c));
540 return allocation_info<T,
decltype(scopedGetter)>{scopedGetter, m_InnerPredictions[I-1]};
547 static auto get(
const std::scoped_allocator_adaptor<As...>& a)
551 return a.outer_allocator();
555 return get<I-1>(a.inner_allocator());
559 predictions_type m_Predictions;
560 std::array<inner_predictions_type, size-1> m_InnerPredictions;
566 class T = std::remove_cvref_t<
typename function_signature<
decltype(&std::remove_cvref_t<Fn>::operator())>::arg>,
567 class Predictions = type_to_allocation_predictions_t<T>,
568 class InnerPredictions = type_to_inner_allocation_predictions_t<T>
570 allocation_info(Fn, Predictions, std::initializer_list<InnerPredictions>)
571 -> allocation_info<T, Fn>;
573 template<top_level TopLevel>
578 constexpr container_counts containers()
const noexcept {
return m_Containers; }
581 : m_Containers{std::move(counts)}
Core components for the Allocation Testing framework.
Utility to convert an initializer_list into an array, potentially transforming the initializer_list i...
Free functions for performing checks, together with the 'checker' class template which wraps them.
Meta-programming utilities.
class template for shifting allocation predictions, especially for MSVC debug builds.
Definition: AllocationCheckers.hpp:217
Definition: AllocationCheckersCore.hpp:73
auto unpack() const
Definition: AllocationCheckers.hpp:527
Base class for use with both plain (shared counting) allocators and std::scoped_allocator_adaptor.
Definition: AllocationCheckers.hpp:371
Class for use with a container possessing a (shared counting) allocator.
Definition: AllocationCheckers.hpp:425
Definition: AllocationCheckers.hpp:575
Definition: AllocationCheckers.hpp:119
A concept for scoped allocators.
Definition: Concepts.hpp:54
Definition: AllocationCheckersTraits.hpp:19
Definition: Utilities.hpp:78
Definition: Utilities.hpp:29
Definition: AllocationCheckers.hpp:335
Definition: AllocationCheckers.hpp:200
Definition: AllocationCheckers.hpp:178
Definition: AllocationCheckers.hpp:175
Definition: AllocationCheckers.hpp:152
Definition: AllocationCheckers.hpp:480
Definition: AllocationCheckers.hpp:351