value_holder.hpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /* Copyright 2016-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_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. * - value_holder ctors accept a first allocator arg passed by
  37. * boost::poly_collection::detail::allocator_adaptor, for purposes
  38. * explained there.
  39. *
  40. * A pointer to value_holder_base<T> can be reinterpret_cast'ed to T*.
  41. * Emplacing is explicitly signalled with value_holder_emplacing_ctor to
  42. * protect us from greedy T's constructible from anything (like
  43. * boost::type_erasure::any).
  44. */
  45. struct value_holder_emplacing_ctor_t{};
  46. constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor=
  47. value_holder_emplacing_ctor_t();
  48. template<typename T>
  49. class value_holder_base
  50. {
  51. protected:
  52. typename std::aligned_storage<sizeof(T),alignof(T)>::type s;
  53. };
  54. template<typename T>
  55. class value_holder:public value_holder_base<T>
  56. {
  57. template<typename U>
  58. using enable_if_not_emplacing_ctor_t=typename std::enable_if<
  59. !std::is_same<
  60. typename std::decay<U>::type,value_holder_emplacing_ctor_t
  61. >::value
  62. >::type*;
  63. using is_nothrow_move_constructible=std::is_nothrow_move_constructible<T>;
  64. using is_copy_constructible=std::is_copy_constructible<T>;
  65. using is_nothrow_copy_constructible=std::is_nothrow_copy_constructible<T>;
  66. using is_move_assignable=std::is_move_assignable<T>;
  67. using is_nothrow_move_assignable=std::is_nothrow_move_assignable<T>;
  68. using is_equality_comparable=detail::is_equality_comparable<T>;
  69. using is_nothrow_equality_comparable=
  70. detail::is_nothrow_equality_comparable<T>;
  71. T* data()noexcept{return reinterpret_cast<T*>(&this->s);}
  72. const T* data()const noexcept
  73. {return reinterpret_cast<const T*>(&this->s);}
  74. T& value()noexcept{return *static_cast<T*>(data());}
  75. const T& value()const noexcept{return *static_cast<const T*>(data());}
  76. public:
  77. template<
  78. typename Allocator,
  79. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  80. >
  81. value_holder(Allocator& al,const T& x)
  82. noexcept(is_nothrow_copy_constructible::value)
  83. {copy(al,x);}
  84. template<
  85. typename Allocator,
  86. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  87. >
  88. value_holder(Allocator& al,T&& x)
  89. noexcept(is_nothrow_move_constructible::value)
  90. {std::allocator_traits<Allocator>::construct(al,data(),std::move(x));}
  91. template<
  92. typename Allocator,typename... Args,
  93. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  94. >
  95. value_holder(Allocator& al,value_holder_emplacing_ctor_t,Args&&... args)
  96. {std::allocator_traits<Allocator>::construct(
  97. al,data(),std::forward<Args>(args)...);}
  98. template<
  99. typename Allocator,
  100. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  101. >
  102. value_holder(Allocator& al,const value_holder& x)
  103. noexcept(is_nothrow_copy_constructible::value)
  104. {copy(al,x.value());}
  105. template<
  106. typename Allocator,
  107. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  108. >
  109. value_holder(Allocator& al,value_holder&& x)
  110. noexcept(is_nothrow_move_constructible::value)
  111. {std::allocator_traits<Allocator>::construct(
  112. al,data(),std::move(x.value()));}
  113. /* stdlib implementations in current use are notoriously lacking at
  114. * complying with [container.requirements.general]/3, so we keep the
  115. * following to make their life easier.
  116. */
  117. value_holder(const T& x)
  118. noexcept(is_nothrow_copy_constructible::value)
  119. {copy(x);}
  120. value_holder(T&& x)
  121. noexcept(is_nothrow_move_constructible::value)
  122. {::new ((void*)data()) T(std::move(x));}
  123. template<typename... Args>
  124. value_holder(value_holder_emplacing_ctor_t,Args&&... args)
  125. {::new ((void*)data()) T(std::forward<Args>(args)...);}
  126. value_holder(const value_holder& x)
  127. noexcept(is_nothrow_copy_constructible::value)
  128. {copy(x.value());}
  129. value_holder(value_holder&& x)
  130. noexcept(is_nothrow_move_constructible::value)
  131. {::new ((void*)data()) T(std::move(x.value()));}
  132. value_holder& operator=(const value_holder& x)=delete;
  133. value_holder& operator=(value_holder&& x)
  134. noexcept(is_nothrow_move_assignable::value||!is_move_assignable::value)
  135. /* if 2nd clause: nothrow move constructibility required */
  136. {
  137. move_assign(std::move(x.value()));
  138. return *this;
  139. }
  140. ~value_holder()noexcept{value().~T();}
  141. friend bool operator==(const value_holder& x,const value_holder& y)
  142. noexcept(is_nothrow_equality_comparable::value)
  143. {
  144. return x.equal(y.value());
  145. }
  146. private:
  147. template<typename Allocator>
  148. void copy(Allocator& al,const T& x){copy(al,x,is_copy_constructible{});}
  149. template<typename Allocator>
  150. void copy(Allocator& al,const T& x,std::true_type)
  151. {
  152. std::allocator_traits<Allocator>::construct(al,data(),x);
  153. }
  154. template<typename Allocator>
  155. void copy(Allocator&,const T&,std::false_type)
  156. {
  157. throw not_copy_constructible{typeid(T)};
  158. }
  159. void copy(const T& x){copy(x,is_copy_constructible{});}
  160. void copy(const T& x,std::true_type)
  161. {
  162. ::new (data()) T(x);
  163. }
  164. void copy(const T&,std::false_type)
  165. {
  166. throw not_copy_constructible{typeid(T)};
  167. }
  168. void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});}
  169. void move_assign(T&& x,std::true_type)
  170. {
  171. value()=std::move(x);
  172. }
  173. void move_assign(T&& x,std::false_type)
  174. {
  175. /* emulated assignment */
  176. static_assert(is_nothrow_move_constructible::value,
  177. "type should be move assignable or nothrow move constructible");
  178. if(data()!=boost::addressof(x)){
  179. value().~T();
  180. ::new (data()) T(std::move(x));
  181. }
  182. }
  183. bool equal(const T& x)const{return equal(x,is_equality_comparable{});}
  184. bool equal(const T& x,std::true_type)const
  185. {
  186. return value()==x;
  187. }
  188. bool equal(const T&,std::false_type)const
  189. {
  190. throw not_equality_comparable{typeid(T)};
  191. }
  192. };
  193. } /* namespace poly_collection::detail */
  194. } /* namespace poly_collection */
  195. } /* namespace boost */
  196. #endif