KTL
fallback.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "../utility/empty_base.h"
4 #include "../utility/meta.h"
5 #include "fallback_fwd.h"
6 #include "type_allocator.h"
7 
8 #include <limits>
9 #include <memory>
10 #include <type_traits>
11 
12 namespace ktl
13 {
21  template<typename P, typename F>
22  class fallback
23  {
24  private:
25  static_assert(detail::has_no_value_type_v<P>, "Building on top of typed allocators is not allowed. Use allocators without a type");
26  static_assert(detail::has_no_value_type_v<F>, "Building on top of typed allocators is not allowed. Use allocators without a type");
27  static_assert(detail::has_owns_v<P>, "The primary allocator is required to have an 'owns(void*)' method");
28 
29  public:
31 
32  fallback() = default;
33 
37  template<typename Primary,
38  typename = std::enable_if_t<std::is_constructible_v<P, Primary>>>
39  explicit fallback(Primary&& primary)
40  noexcept(std::is_nothrow_constructible_v<P, Primary> && std::is_nothrow_default_constructible_v<F>) :
41  m_Primary(std::forward<Primary>(primary)),
42  m_Fallback() {}
43 
47  template<typename Primary, typename Fallback,
48  typename = std::enable_if_t<
49  std::is_constructible_v<P, Primary> &&
50  std::is_constructible_v<F, Fallback>>>
51  explicit fallback(Primary&& primary, Fallback&& fallback)
52  noexcept(std::is_nothrow_constructible_v<P, Primary> && std::is_nothrow_constructible_v<F, Fallback>) :
53  m_Primary(std::forward<Primary>(primary)),
54  m_Fallback(std::forward<Fallback>(fallback)) {}
55 
59  template<typename... Args,
60  typename = std::enable_if_t<
61  std::is_constructible_v<P, Args...>>>
62  explicit fallback(std::tuple<Args...>&& primary)
63  noexcept(std::is_nothrow_constructible_v<P, Args...> && std::is_nothrow_default_constructible_v<F>) :
64  m_Primary(std::make_from_tuple<P>(std::forward<std::tuple<Args...>>(primary))),
65  m_Fallback() {}
66 
70  template<typename... ArgsP, typename... ArgsF,
71  typename = std::enable_if_t<
72  std::is_constructible_v<P, ArgsP...>&&
73  std::is_constructible_v<F, ArgsF...>>>
74  explicit fallback(std::tuple<ArgsP...>&& primary, std::tuple<ArgsF...>&& fallback)
75  noexcept(std::is_nothrow_constructible_v<P, ArgsP...> && std::is_nothrow_constructible_v<F, ArgsF...>) :
76  m_Primary(std::make_from_tuple<P>(std::forward<std::tuple<ArgsP...>>(primary))),
77  m_Fallback(std::make_from_tuple<F>(std::forward<std::tuple<ArgsF...>>(fallback))) {}
78 
79  fallback(const fallback&) = default;
80 
81  fallback(fallback&&) = default;
82 
83  fallback& operator=(const fallback&) = default;
84 
85  fallback& operator=(fallback&&) = default;
86 
87  bool operator==(const fallback& rhs) const
88  noexcept(detail::has_nothrow_equal_v<P> && detail::has_nothrow_equal_v<F>)
89  {
90  return m_Primary == rhs.m_Primary && m_Fallback == rhs.m_Fallback;
91  }
92 
93  bool operator!=(const fallback& rhs) const
94  noexcept(detail::has_nothrow_not_equal_v<P>&& detail::has_nothrow_not_equal_v<F>)
95  {
96  return m_Primary != rhs.m_Primary || m_Fallback != rhs.m_Fallback;
97  }
98 
99 #pragma region Allocation
100  void* allocate(size_t n)
101  noexcept(detail::has_nothrow_allocate_v<P> && detail::has_nothrow_allocate_v<F>)
102  {
103  void* ptr = m_Primary.allocate(n);
104  if (!ptr)
105  return m_Fallback.allocate(n);
106  return ptr;
107  }
108 
109  void deallocate(void* p, size_t n)
110  noexcept(detail::has_nothrow_deallocate_v<P>&& detail::has_nothrow_deallocate_v<F>)
111  {
112  if (m_Primary.owns(p))
113  {
114  m_Primary.deallocate(p, n);
115  return;
116  }
117 
118  m_Fallback.deallocate(p, n);
119  }
120 #pragma endregion
121 
122 #pragma region Construction
123  template<typename T, typename... Args>
124  typename std::enable_if<detail::has_construct_v<P, T*, Args...> || detail::has_construct_v<F, T*, Args...>, void>::type
125  construct(T* p, Args&&... args) noexcept(
126  (!detail::has_construct_v<P, T*, Args...> || detail::has_nothrow_construct_v<P, T*, Args...>) &&
127  (!detail::has_construct_v<F, T*, Args...> || detail::has_nothrow_construct_v<F, T*, Args...>) &&
128  std::is_nothrow_constructible_v<T, Args...>)
129  {
130  bool owned = m_Primary.owns(p);
131 
132  if constexpr (detail::has_construct_v<P, T*, Args...>)
133  {
134  if (owned)
135  {
136  m_Primary.construct(p, std::forward<Args>(args)...);
137  return;
138  }
139  }
140 
141  if constexpr (detail::has_construct_v<F, T*, Args...>)
142  {
143  if (!owned)
144  {
145  m_Fallback.construct(p, std::forward<Args>(args)...);
146  return;
147  }
148  }
149 
150  ::new(p) T(std::forward<Args>(args)...);
151  }
152 
153  template<typename T>
154  typename std::enable_if<detail::has_destroy_v<P, T*> || detail::has_destroy_v<F, T*>, void>::type
155  destroy(T* p) noexcept(
156  (!detail::has_destroy_v<P, T*> || detail::has_nothrow_destroy_v<P, T*>) &&
157  (!detail::has_destroy_v<F, T*> || detail::has_nothrow_destroy_v<F, T*>) &&
158  std::is_nothrow_destructible_v<T>)
159  {
160  bool owned = m_Primary.owns(p);
161 
162  if constexpr (detail::has_destroy_v<P, T*>)
163  {
164  if (owned)
165  {
166  m_Primary.destroy(p);
167  return;
168  }
169  }
170 
171  if constexpr (detail::has_destroy_v<F, T*>)
172  {
173  if (!owned)
174  {
175  m_Fallback.destroy(p);
176  return;
177  }
178  }
179 
180  p->~T();
181  }
182 #pragma endregion
183 
184 #pragma region Utility
185  template<typename Primary = P, typename Fallback = F>
186  typename std::enable_if<detail::has_max_size_v<Primary> && detail::has_max_size_v<Fallback>, size_type>::type
187  max_size() const
188  noexcept(detail::has_nothrow_max_size_v<Primary> && detail::has_nothrow_max_size_v<Fallback>)
189  {
190  return (std::max)(m_Primary.max_size(), m_Fallback.max_size());
191  }
192 
193  template<typename Primary = P, typename Fallback = F>
194  typename std::enable_if<detail::has_owns_v<Primary> && detail::has_owns_v<Fallback>, bool>::type
195  owns(void* p) const
196  noexcept(detail::has_nothrow_owns_v<Primary> && detail::has_nothrow_owns_v<Fallback>)
197  {
198  if (m_Primary.owns(p))
199  return true;
200 
201  return m_Fallback.owns(p);
202  }
203 #pragma endregion
204 
205  private:
206  KTL_EMPTY_BASE P m_Primary;
207  KTL_EMPTY_BASE F m_Fallback;
208  };
209 }
An allocator which delegates allocations between 2 different allocators. It first attempts to allocat...
Definition: fallback.h:23
fallback(std::tuple< ArgsP... > &&primary, std::tuple< ArgsF... > &&fallback) noexcept(std::is_nothrow_constructible_v< P, ArgsP... > &&std::is_nothrow_constructible_v< F, ArgsF... >)
Constructor for forwarding a tuple of arguments to the primary and fallback allocators.
Definition: fallback.h:74
fallback(fallback &&)=default
void * allocate(size_t n) noexcept(detail::has_nothrow_allocate_v< P > &&detail::has_nothrow_allocate_v< F >)
Definition: fallback.h:100
fallback(std::tuple< Args... > &&primary) noexcept(std::is_nothrow_constructible_v< P, Args... > &&std::is_nothrow_default_constructible_v< F >)
Constructor for forwarding a tuple of arguments to the primary allocator.
Definition: fallback.h:62
fallback & operator=(fallback &&)=default
bool operator!=(const fallback &rhs) const noexcept(detail::has_nothrow_not_equal_v< P > &&detail::has_nothrow_not_equal_v< F >)
Definition: fallback.h:93
fallback()=default
fallback(const fallback &)=default
fallback(Primary &&primary, Fallback &&fallback) noexcept(std::is_nothrow_constructible_v< P, Primary > &&std::is_nothrow_constructible_v< F, Fallback >)
Constructor for forwarding a single argument to the primary and fallback allocators.
Definition: fallback.h:51
bool operator==(const fallback &rhs) const noexcept(detail::has_nothrow_equal_v< P > &&detail::has_nothrow_equal_v< F >)
Definition: fallback.h:87
std::enable_if< detail::has_destroy_v< P, T * >||detail::has_destroy_v< F, T * >, void >::type destroy(T *p) noexcept((!detail::has_destroy_v< P, T * >||detail::has_nothrow_destroy_v< P, T * >) &&(!detail::has_destroy_v< F, T * >||detail::has_nothrow_destroy_v< F, T * >) &&std::is_nothrow_destructible_v< T >)
Definition: fallback.h:155
std::enable_if< detail::has_max_size_v< Primary > &&detail::has_max_size_v< Fallback >, size_type >::type max_size() const noexcept(detail::has_nothrow_max_size_v< Primary > &&detail::has_nothrow_max_size_v< Fallback >)
Definition: fallback.h:187
std::enable_if< detail::has_owns_v< Primary > &&detail::has_owns_v< Fallback >, bool >::type owns(void *p) const noexcept(detail::has_nothrow_owns_v< Primary > &&detail::has_nothrow_owns_v< Fallback >)
Definition: fallback.h:195
void deallocate(void *p, size_t n) noexcept(detail::has_nothrow_deallocate_v< P > &&detail::has_nothrow_deallocate_v< F >)
Definition: fallback.h:109
detail::get_size_type_t< P > size_type
Definition: fallback.h:25
std::enable_if< detail::has_construct_v< P, T *, Args... >||detail::has_construct_v< F, T *, Args... >, void >::type construct(T *p, Args &&... args) noexcept((!detail::has_construct_v< P, T *, Args... >||detail::has_nothrow_construct_v< P, T *, Args... >) &&(!detail::has_construct_v< F, T *, Args... >||detail::has_nothrow_construct_v< F, T *, Args... >) &&std::is_nothrow_constructible_v< T, Args... >)
Definition: fallback.h:125
fallback & operator=(const fallback &)=default
fallback(Primary &&primary) noexcept(std::is_nothrow_constructible_v< P, Primary > &&std::is_nothrow_default_constructible_v< F >)
Constructor for forwarding a single argument to the primary allocator.
Definition: fallback.h:39
#define KTL_EMPTY_BASE
Definition: empty_base.h:6
constexpr bool has_construct_v
Definition: meta.h:41
typename get_size_type< Alloc, void >::type get_size_type_t
Definition: meta.h:31
constexpr bool has_nothrow_max_size_v
Definition: meta.h:122
Definition: cascading.h:15