allocator_adaptor.hpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /* Copyright 2018 Joaquin M Lopez Munoz.
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * http://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * See http://www.boost.org/libs/poly_collection for library home page.
  7. */
  8. #ifndef BOOST_POLY_COLLECTION_DETAIL_ALLOCATOR_ADAPTOR_HPP
  9. #define BOOST_POLY_COLLECTION_DETAIL_ALLOCATOR_ADAPTOR_HPP
  10. #if defined(_MSC_VER)
  11. #pragma once
  12. #endif
  13. #include <boost/mp11/function.hpp>
  14. #include <boost/mp11/integer_sequence.hpp>
  15. #include <boost/poly_collection/detail/is_constructible.hpp>
  16. #include <new>
  17. #include <memory>
  18. #include <tuple>
  19. #include <type_traits>
  20. #include <utility>
  21. namespace boost{
  22. namespace poly_collection{
  23. namespace detail{
  24. /* [container.requirements.general]/3 state that containers must use the
  25. * allocator's construct/destroy member functions to construct/destroy
  26. * elements and *not at all* for any other type. Since poly_collection is
  27. * implemented as a multi-level structure of container and container-like
  28. * objects, we need to use an adaptor for the user-provided Allocator that
  29. * prevents intermediate entities from calling Allocator::[construct|destroy].
  30. * allocator_adaptor<Allocator> does this by taking advantage of the fact that
  31. * elements are ultimately held within a value_holder:
  32. * - construct(value_holder<T>*,...) uses placement new construction and
  33. * passes the wrapped Allocator object for value_holder<T> to use for
  34. * its inner construction of T.
  35. * - For the rest of types, construct uses placement new construction and
  36. * passes down the adaptor object itself as an argument following an
  37. * approach analogous to that of std::scoped_allocator_adaptor.
  38. * - destroy(value_holder<T>) resorts to Allocator::destroy to destroy the
  39. * contained T element.
  40. * - For the rest of types, destroy(T) calls ~T directly.
  41. *
  42. * Code has been ripped and adapted from libc++'s implementation of
  43. * std::scoped_allocator_adaptor.
  44. */
  45. template<typename T>
  46. class value_holder_base;
  47. template<typename T>
  48. class value_holder;
  49. template<typename T,typename Allocator,typename... Args>
  50. struct uses_alloc_ctor_impl
  51. {
  52. using RawAllocator=typename std::remove_cv<
  53. typename std::remove_reference<Allocator>::type
  54. >::type;
  55. static const bool ua=std::uses_allocator<T,RawAllocator>::value;
  56. static const int ic=is_constructible<
  57. T,std::allocator_arg_t,Allocator,Args...>::value?1:0;
  58. static const int value=ua?2-ic:0;
  59. };
  60. template<typename T,typename Allocator,typename... Args>
  61. struct uses_alloc_ctor:
  62. std::integral_constant<int,uses_alloc_ctor_impl<T,Allocator,Args...>::value>
  63. {};
  64. template<typename Allocator,typename=void>
  65. struct allocator_is_always_equal:std::is_empty<Allocator>{};
  66. template<typename Allocator>
  67. struct allocator_is_always_equal<
  68. Allocator,
  69. mp11::mp_void<
  70. typename std::allocator_traits<Allocator>::is_always_equal
  71. >
  72. >:std::allocator_traits<Allocator>::is_always_equal{};
  73. template<typename Allocator>
  74. struct allocator_adaptor:Allocator
  75. {
  76. using traits=std::allocator_traits<Allocator>;
  77. using value_type=typename traits::value_type;
  78. using size_type=typename traits::size_type;
  79. using difference_type=typename traits::difference_type;
  80. using pointer=typename traits::pointer;
  81. using const_pointer=typename traits::const_pointer;
  82. using void_pointer=typename traits::void_pointer;
  83. using const_void_pointer=typename traits::const_void_pointer;
  84. using propagate_on_container_copy_assignment=
  85. typename traits::propagate_on_container_copy_assignment;
  86. using propagate_on_container_move_assignment=
  87. typename traits::propagate_on_container_move_assignment;
  88. using propagate_on_container_swap=
  89. typename traits::propagate_on_container_swap;
  90. using is_always_equal=typename allocator_is_always_equal<Allocator>::type;
  91. template<typename U>
  92. struct rebind
  93. {
  94. using other=allocator_adaptor<typename traits::template rebind_alloc<U>>;
  95. };
  96. allocator_adaptor()=default;
  97. allocator_adaptor(const allocator_adaptor&)=default;
  98. template<
  99. typename Allocator2,
  100. typename std::enable_if<
  101. is_constructible<Allocator,const Allocator2&>::value
  102. >::type* =nullptr
  103. >
  104. allocator_adaptor(const Allocator2& x)noexcept:Allocator{x}{}
  105. template<
  106. typename Allocator2,
  107. typename std::enable_if<
  108. is_constructible<Allocator,const Allocator2&>::value
  109. >::type* =nullptr
  110. >
  111. allocator_adaptor(const allocator_adaptor<Allocator2>& x)noexcept:
  112. Allocator{x.allocator()}{}
  113. allocator_adaptor& operator=(const allocator_adaptor&)=default;
  114. Allocator& allocator()noexcept{return *this;}
  115. const Allocator& allocator()const noexcept{return *this;}
  116. template<typename T,typename... Args>
  117. void construct(T* p,Args&&... args)
  118. {
  119. construct_(
  120. uses_alloc_ctor<T,allocator_adaptor&,Args...>{},
  121. p,std::forward<Args>(args)...);
  122. }
  123. template<typename T,typename... Args>
  124. void construct(value_holder<T>* p,Args&&... args)
  125. {
  126. ::new ((void*)p) value_holder<T>(allocator(),std::forward<Args>(args)...);
  127. }
  128. template<typename T1,typename T2,typename... Args1,typename... Args2>
  129. void construct(
  130. std::pair<T1,T2>* p,std::piecewise_construct_t,
  131. std::tuple<Args1...> x,std::tuple<Args2...> y)
  132. {
  133. ::new ((void*)p) std::pair<T1,T2>(
  134. std::piecewise_construct,
  135. transform_tuple(
  136. uses_alloc_ctor<T1,allocator_adaptor&,Args1...>{},
  137. std::move(x),
  138. mp11::make_index_sequence<sizeof...(Args1)>{}),
  139. transform_tuple(
  140. uses_alloc_ctor<T2,allocator_adaptor&,Args2...>{},
  141. std::move(y),
  142. mp11::make_index_sequence<sizeof...(Args2)>{})
  143. );
  144. }
  145. template<typename T1,typename T2>
  146. void construct(std::pair<T1,T2>* p)
  147. {
  148. construct(p,std::piecewise_construct,std::tuple<>{},std::tuple<>{});
  149. }
  150. template<typename T1,typename T2,typename U,typename V>
  151. void construct(std::pair<T1,T2>* p,U&& x,V&& y)
  152. {
  153. construct(
  154. p,std::piecewise_construct,
  155. std::forward_as_tuple(std::forward<U>(x)),
  156. std::forward_as_tuple(std::forward<V>(y)));
  157. }
  158. template<typename T1,typename T2,typename U,typename V>
  159. void construct(std::pair<T1,T2>* p,const std::pair<U,V>& x)
  160. {
  161. construct(
  162. p,std::piecewise_construct,
  163. std::forward_as_tuple(x.first),std::forward_as_tuple(x.second));
  164. }
  165. template<typename T1,typename T2,typename U,typename V>
  166. void construct(std::pair<T1,T2>* p,std::pair<U,V>&& x)
  167. {
  168. construct(
  169. p,std::piecewise_construct,
  170. std::forward_as_tuple(std::forward<U>(x.first)),
  171. std::forward_as_tuple(std::forward<V>(x.second)));
  172. }
  173. template<typename T>
  174. void destroy(T* p)
  175. {
  176. p->~T();
  177. }
  178. template<typename T>
  179. void destroy(value_holder<T>* p)
  180. {
  181. traits::destroy(
  182. allocator(),
  183. reinterpret_cast<T*>(static_cast<value_holder_base<T>*>(p)));
  184. }
  185. allocator_adaptor
  186. select_on_container_copy_construction()const noexcept
  187. {
  188. return traits::select_on_container_copy_construction(allocator());
  189. }
  190. private:
  191. template<typename T,typename... Args>
  192. void construct_(
  193. std::integral_constant<int,0>, /* doesn't use allocator */
  194. T* p,Args&&... args)
  195. {
  196. ::new ((void*)p) T(std::forward<Args>(args)...);
  197. }
  198. template<typename T,typename... Args>
  199. void construct_(
  200. std::integral_constant<int,1>, /* with std::allocator_arg */
  201. T* p,Args&&... args)
  202. {
  203. ::new ((void*)p) T(std::allocator_arg,*this,std::forward<Args>(args)...);
  204. }
  205. template<typename T,typename... Args>
  206. void construct_(
  207. std::integral_constant<int,2>, /* allocator at the end */
  208. T* p,Args&&... args)
  209. {
  210. ::new ((void*)p) T(std::forward<Args>(args)...,*this);
  211. }
  212. template<typename... Args,std::size_t... I>
  213. std::tuple<Args&&...> transform_tuple(
  214. std::integral_constant<int,0>, /* doesn't use allocator */
  215. std::tuple<Args...>&& t,mp11::index_sequence<I...>)
  216. {
  217. return std::tuple<Args&&...>(std::get<I>(std::move(t))...);
  218. }
  219. template<typename... Args,std::size_t... I>
  220. std::tuple<std::allocator_arg_t,allocator_adaptor&,Args&&...>
  221. transform_tuple(
  222. std::integral_constant<int,1>, /* with std::allocator_arg */
  223. std::tuple<Args...>&& t,mp11::index_sequence<I...>)
  224. {
  225. return std::tuple<
  226. std::allocator_arg_t,allocator_adaptor&,Args&&...>(
  227. std::allocator_arg,*this,std::get<I>(std::move(t))...);
  228. }
  229. template<typename... Args,std::size_t... I>
  230. std::tuple<Args&&...,allocator_adaptor&>
  231. transform_tuple(
  232. std::integral_constant<int,2>, /* allocator at the end */
  233. std::tuple<Args...>&& t,mp11::index_sequence<I...>)
  234. {
  235. return std::tuple<Args&&...,allocator_adaptor&>(
  236. std::get<I>(std::move(t))...,*this);
  237. }
  238. };
  239. template<typename Allocator1,typename Allocator2>
  240. bool operator==(
  241. const allocator_adaptor<Allocator1>& x,
  242. const allocator_adaptor<Allocator2>& y)noexcept
  243. {
  244. return x.allocator()==y.allocator();
  245. }
  246. template<typename Allocator1,typename Allocator2>
  247. bool operator!=(
  248. const allocator_adaptor<Allocator1>& x,
  249. const allocator_adaptor<Allocator2>& y)noexcept
  250. {
  251. return !(x==y);
  252. }
  253. } /* namespace poly_collection::detail */
  254. } /* namespace poly_collection */
  255. } /* namespace boost */
  256. #endif