value_holder.hpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /* Copyright 2016-2017 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_VALUE_HOLDER_HPP
  9. #define BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP
  10. #if defined(_MSC_VER)
  11. #pragma once
  12. #endif
  13. #include <boost/core/addressof.hpp>
  14. #include <boost/poly_collection/detail/is_constructible.hpp>
  15. #include <boost/poly_collection/detail/is_equality_comparable.hpp>
  16. #include <boost/poly_collection/detail/is_nothrow_eq_comparable.hpp>
  17. #include <boost/poly_collection/exception.hpp>
  18. #include <new>
  19. #include <memory>
  20. #include <type_traits>
  21. #include <utility>
  22. namespace boost{
  23. namespace poly_collection{
  24. namespace detail{
  25. /* Segments of a poly_collection maintain vectors of value_holder<T>
  26. * rather than directly T. This serves several purposes:
  27. * - value_holder<T> is copy constructible and equality comparable even if T
  28. * is not: executing the corresponding op results in a reporting exception
  29. * being thrown. This allows the segment to offer its full virtual
  30. * interface regardless of the properties of the concrete class contained.
  31. * - value_holder<T> emulates move assignment when T is not move assignable
  32. * (nothrow move constructibility required); this happens most notably with
  33. * lambda functions, whose assignment operator is deleted by standard
  34. * mandate [expr.prim.lambda]/20 even if the compiler generated one would
  35. * work (capture by value).
  36. * - To comply with [container.requirements.general]/3 (or, more precisely,
  37. * its natural extension to polymorphic containers), value_holder ctors
  38. * accept a first allocator arg passed by value_holder_allocator_adaptor,
  39. * which must therefore be used in the vectors of value_holder's.
  40. *
  41. * A pointer to value_holder_base<T> can be reinterpret_cast'ed to T*.
  42. * Emplacing is explicitly signalled with value_holder_emplacing_ctor to
  43. * protect us from greedy T's constructible from anything (like
  44. * boost::type_erasure::any).
  45. */
  46. struct value_holder_emplacing_ctor_t{};
  47. constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor=
  48. value_holder_emplacing_ctor_t();
  49. template<typename T>
  50. class value_holder_base
  51. {
  52. protected:
  53. typename std::aligned_storage<sizeof(T),alignof(T)>::type s;
  54. };
  55. template<typename T>
  56. class value_holder:public value_holder_base<T>
  57. {
  58. template<typename U>
  59. using enable_if_not_emplacing_ctor_t=typename std::enable_if<
  60. !std::is_same<
  61. typename std::decay<U>::type,value_holder_emplacing_ctor_t
  62. >::value
  63. >::type*;
  64. using is_nothrow_move_constructible=std::is_nothrow_move_constructible<T>;
  65. using is_copy_constructible=std::is_copy_constructible<T>;
  66. using is_nothrow_copy_constructible=std::is_nothrow_copy_constructible<T>;
  67. using is_move_assignable=std::is_move_assignable<T>;
  68. using is_nothrow_move_assignable=std::is_nothrow_move_assignable<T>;
  69. using is_equality_comparable=detail::is_equality_comparable<T>;
  70. using is_nothrow_equality_comparable=
  71. detail::is_nothrow_equality_comparable<T>;
  72. T* data()noexcept{return reinterpret_cast<T*>(&this->s);}
  73. const T* data()const noexcept
  74. {return reinterpret_cast<const T*>(&this->s);}
  75. T& value()noexcept{return *static_cast<T*>(data());}
  76. const T& value()const noexcept{return *static_cast<const T*>(data());}
  77. public:
  78. template<
  79. typename Allocator,
  80. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  81. >
  82. value_holder(Allocator& al,const T& x)
  83. noexcept(is_nothrow_copy_constructible::value)
  84. {copy(al,x);}
  85. template<
  86. typename Allocator,
  87. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  88. >
  89. value_holder(Allocator& al,T&& x)
  90. noexcept(is_nothrow_move_constructible::value)
  91. {std::allocator_traits<Allocator>::construct(al,data(),std::move(x));}
  92. template<
  93. typename Allocator,typename... Args,
  94. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  95. >
  96. value_holder(Allocator& al,value_holder_emplacing_ctor_t,Args&&... args)
  97. {std::allocator_traits<Allocator>::construct(
  98. al,data(),std::forward<Args>(args)...);}
  99. template<
  100. typename Allocator,
  101. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  102. >
  103. value_holder(Allocator& al,const value_holder& x)
  104. noexcept(is_nothrow_copy_constructible::value)
  105. {copy(al,x.value());}
  106. template<
  107. typename Allocator,
  108. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  109. >
  110. value_holder(Allocator& al,value_holder&& x)
  111. noexcept(is_nothrow_move_constructible::value)
  112. {std::allocator_traits<Allocator>::construct(
  113. al,data(),std::move(x.value()));}
  114. /* stdlib implementations in current use are notoriously lacking at
  115. * complying with [container.requirements.general]/3, so we keep the
  116. * following to make their life easier.
  117. */
  118. value_holder(const T& x)
  119. noexcept(is_nothrow_copy_constructible::value)
  120. {copy(x);}
  121. value_holder(T&& x)
  122. noexcept(is_nothrow_move_constructible::value)
  123. {::new ((void*)data()) T(std::move(x));}
  124. template<typename... Args>
  125. value_holder(value_holder_emplacing_ctor_t,Args&&... args)
  126. {::new ((void*)data()) T(std::forward<Args>(args)...);}
  127. value_holder(const value_holder& x)
  128. noexcept(is_nothrow_copy_constructible::value)
  129. {copy(x.value());}
  130. value_holder(value_holder&& x)
  131. noexcept(is_nothrow_move_constructible::value)
  132. {::new ((void*)data()) T(std::move(x.value()));}
  133. value_holder& operator=(const value_holder& x)=delete;
  134. value_holder& operator=(value_holder&& x)
  135. noexcept(is_nothrow_move_assignable::value||!is_move_assignable::value)
  136. /* if 2nd clause: nothrow move constructibility required */
  137. {
  138. move_assign(std::move(x.value()));
  139. return *this;
  140. }
  141. ~value_holder()noexcept{value().~T();}
  142. friend bool operator==(const value_holder& x,const value_holder& y)
  143. noexcept(is_nothrow_equality_comparable::value)
  144. {
  145. return x.equal(y.value());
  146. }
  147. private:
  148. template<typename Allocator>
  149. void copy(Allocator& al,const T& x){copy(al,x,is_copy_constructible{});}
  150. template<typename Allocator>
  151. void copy(Allocator& al,const T& x,std::true_type)
  152. {
  153. std::allocator_traits<Allocator>::construct(al,data(),x);
  154. }
  155. template<typename Allocator>
  156. void copy(Allocator&,const T&,std::false_type)
  157. {
  158. throw not_copy_constructible{typeid(T)};
  159. }
  160. void copy(const T& x){copy(x,is_copy_constructible{});}
  161. void copy(const T& x,std::true_type)
  162. {
  163. ::new (data()) T(x);
  164. }
  165. void copy(const T&,std::false_type)
  166. {
  167. throw not_copy_constructible{typeid(T)};
  168. }
  169. void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});}
  170. void move_assign(T&& x,std::true_type)
  171. {
  172. value()=std::move(x);
  173. }
  174. void move_assign(T&& x,std::false_type)
  175. {
  176. /* emulated assignment */
  177. static_assert(is_nothrow_move_constructible::value,
  178. "type should be move assignable or nothrow move constructible");
  179. if(data()!=boost::addressof(x)){
  180. value().~T();
  181. ::new (data()) T(std::move(x));
  182. }
  183. }
  184. bool equal(const T& x)const{return equal(x,is_equality_comparable{});}
  185. bool equal(const T& x,std::true_type)const
  186. {
  187. return value()==x;
  188. }
  189. bool equal(const T&,std::false_type)const
  190. {
  191. throw not_equality_comparable{typeid(T)};
  192. }
  193. };
  194. template<typename Allocator>
  195. struct value_holder_allocator_adaptor:Allocator
  196. {
  197. using traits=std::allocator_traits<Allocator>;
  198. using value_type=typename traits::value_type;
  199. using size_type=typename traits::size_type;
  200. using difference_type=typename traits::difference_type;
  201. using pointer=typename traits::pointer;
  202. using const_pointer=typename traits::const_pointer;
  203. using void_pointer=typename traits::void_pointer;
  204. using const_void_pointer=typename traits::const_void_pointer;
  205. using propagate_on_container_copy_assignment=
  206. typename traits::propagate_on_container_copy_assignment;
  207. using propagate_on_container_move_assignment=
  208. typename traits::propagate_on_container_move_assignment;
  209. using propagate_on_container_swap=
  210. typename traits::propagate_on_container_swap;
  211. template<typename U>
  212. struct rebind
  213. {
  214. using other=value_holder_allocator_adaptor<
  215. typename traits::template rebind_alloc<U>>;
  216. };
  217. value_holder_allocator_adaptor()=default;
  218. value_holder_allocator_adaptor(
  219. const value_holder_allocator_adaptor&)=default;
  220. template<
  221. typename Allocator2,
  222. typename std::enable_if<
  223. is_constructible<Allocator,Allocator2>::value
  224. >::type* =nullptr
  225. >
  226. value_holder_allocator_adaptor(const Allocator2& x)noexcept:Allocator{x}{}
  227. template<
  228. typename Allocator2,
  229. typename std::enable_if<
  230. is_constructible<Allocator,Allocator2>::value
  231. >::type* =nullptr
  232. >
  233. value_holder_allocator_adaptor(
  234. const value_holder_allocator_adaptor<Allocator2>& x)noexcept:
  235. Allocator{static_cast<const Allocator2&>(x)}{}
  236. value_holder_allocator_adaptor& operator=(
  237. const value_holder_allocator_adaptor&)=default;
  238. template<typename T,typename... Args>
  239. void construct(T* p,Args&&... args)
  240. {
  241. ::new ((void*)p) T(std::forward<Args>(args)...);
  242. }
  243. template<typename T,typename... Args>
  244. void construct(value_holder<T>* p,Args&&... args)
  245. {
  246. ::new ((void*)p) value_holder<T>(
  247. static_cast<Allocator&>(*this),std::forward<Args>(args)...);
  248. }
  249. template<typename T>
  250. void destroy(T* p)
  251. {
  252. p->~T();
  253. }
  254. template<typename T>
  255. void destroy(value_holder<T>* p)
  256. {
  257. traits::destroy(
  258. static_cast<Allocator&>(*this),
  259. reinterpret_cast<T*>(static_cast<value_holder_base<T>*>(p)));
  260. }
  261. };
  262. } /* namespace poly_collection::detail */
  263. } /* namespace poly_collection */
  264. } /* namespace boost */
  265. #endif