value_holder.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /* Copyright 2016-2024 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/poly_collection/detail/is_constructible.hpp>
  14. #include <boost/poly_collection/detail/is_equality_comparable.hpp>
  15. #include <boost/poly_collection/detail/is_nothrow_eq_comparable.hpp>
  16. #include <boost/poly_collection/exception.hpp>
  17. #include <new>
  18. #include <memory>
  19. #include <type_traits>
  20. #include <utility>
  21. namespace boost{
  22. namespace poly_collection{
  23. namespace detail{
  24. /* Segments of a poly_collection maintain vectors of value_holder<T>
  25. * rather than directly T. This serves several purposes:
  26. * - value_holder<T> is copy constructible and equality comparable even if T
  27. * is not: executing the corresponding op results in a reporting exception
  28. * being thrown. This allows the segment to offer its full virtual
  29. * interface regardless of the properties of the concrete class contained.
  30. * - value_holder<T> emulates move assignment when T is not move assignable
  31. * (nothrow move constructibility required); this happens most notably with
  32. * lambda functions, whose assignment operator is deleted by standard
  33. * mandate [expr.prim.lambda]/20 even if the compiler generated one would
  34. * work (capture by value).
  35. * - value_holder ctors accept a first allocator arg passed by
  36. * boost::poly_collection::detail::allocator_adaptor, for purposes
  37. * explained there.
  38. *
  39. * A pointer to value_holder_base<T> can be reinterpret_cast'ed to T*.
  40. * Emplacing is explicitly signalled with value_holder_emplacing_ctor to
  41. * protect us from greedy T's constructible from anything (like
  42. * boost::type_erasure::any).
  43. *
  44. * If specified, the second arg in value_holder<T,U> is a type
  45. * from where T will be copy/move constructed, rather than T itself.
  46. * T must be (nothrow) copy/move constructible iff it is (nothrow)
  47. * constructible from const U&/U&&. This serves the use case where a segment
  48. * for U does not store Us but objects of some other type (=T) constructed
  49. * from them.
  50. */
  51. struct value_holder_emplacing_ctor_t{};
  52. constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor=
  53. value_holder_emplacing_ctor_t();
  54. template<typename T>
  55. class value_holder_base
  56. {
  57. protected:
  58. alignas(T) unsigned char s[sizeof(T)];
  59. };
  60. template<typename T,typename U=T>
  61. class value_holder:public value_holder_base<T>
  62. {
  63. template<typename Q>
  64. using enable_if_not_emplacing_ctor_t=typename std::enable_if<
  65. !std::is_same<
  66. typename std::decay<Q>::type,value_holder_emplacing_ctor_t
  67. >::value
  68. >::type*;
  69. using is_nothrow_move_constructible=std::is_nothrow_move_constructible<U>;
  70. using is_copy_constructible=std::is_copy_constructible<U>;
  71. using is_nothrow_copy_constructible=std::is_nothrow_copy_constructible<U>;
  72. using is_move_assignable=std::is_move_assignable<U>;
  73. using is_nothrow_move_assignable=std::is_nothrow_move_assignable<U>;
  74. using is_equality_comparable=detail::is_equality_comparable<U>;
  75. using is_nothrow_equality_comparable=detail::is_nothrow_equality_comparable<U>;
  76. T* data()noexcept{return reinterpret_cast<T*>(&this->s);}
  77. const T* data()const noexcept
  78. {return reinterpret_cast<const T*>(&this->s);}
  79. T& value()noexcept{return *static_cast<T*>(data());}
  80. const T& value()const noexcept{return *static_cast<const T*>(data());}
  81. public:
  82. template<
  83. typename Allocator,
  84. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  85. >
  86. value_holder(Allocator& al,const U& x)
  87. noexcept(is_nothrow_copy_constructible::value)
  88. {allocator_copy(al,x);}
  89. template<
  90. typename Allocator,
  91. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  92. >
  93. value_holder(Allocator& al,U&& x)
  94. noexcept(is_nothrow_move_constructible::value)
  95. {std::allocator_traits<Allocator>::construct(al,data(),std::move(x));}
  96. template<
  97. typename Allocator,typename... Args,
  98. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  99. >
  100. value_holder(Allocator& al,value_holder_emplacing_ctor_t,Args&&... args)
  101. {std::allocator_traits<Allocator>::construct(
  102. al,data(),std::forward<Args>(args)...);}
  103. template<
  104. typename Allocator,
  105. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  106. >
  107. value_holder(Allocator& al,const value_holder& x)
  108. noexcept(is_nothrow_copy_constructible::value)
  109. {allocator_copy(al,x.value());}
  110. template<
  111. typename Allocator,
  112. enable_if_not_emplacing_ctor_t<Allocator> =nullptr
  113. >
  114. value_holder(Allocator& al,value_holder&& x)
  115. noexcept(is_nothrow_move_constructible::value)
  116. {std::allocator_traits<Allocator>::construct(
  117. al,data(),std::move(x.value()));}
  118. /* stdlib implementations in current use are notoriously lacking at
  119. * complying with [container.requirements.general]/3, so we keep the
  120. * following to make their life easier.
  121. */
  122. value_holder(const U& x)
  123. noexcept(is_nothrow_copy_constructible::value)
  124. {copy(x);}
  125. value_holder(U&& x)
  126. noexcept(is_nothrow_move_constructible::value)
  127. {::new ((void*)data()) T(std::move(x));}
  128. template<typename... Args>
  129. value_holder(value_holder_emplacing_ctor_t,Args&&... args)
  130. {::new ((void*)data()) T(std::forward<Args>(args)...);}
  131. value_holder(const value_holder& x)
  132. noexcept(is_nothrow_copy_constructible::value)
  133. {copy(x.value());}
  134. value_holder(value_holder&& x)
  135. noexcept(is_nothrow_move_constructible::value)
  136. {::new ((void*)data()) T(std::move(x.value()));}
  137. value_holder& operator=(const value_holder& x)=delete;
  138. value_holder& operator=(value_holder&& x)
  139. noexcept(is_nothrow_move_assignable::value||!is_move_assignable::value)
  140. /* if 2nd clause: nothrow move constructibility required */
  141. {
  142. move_assign(std::move(x.value()));
  143. return *this;
  144. }
  145. ~value_holder()noexcept{value().~T();}
  146. friend bool operator==(const value_holder& x,const value_holder& y)
  147. noexcept(is_nothrow_equality_comparable::value)
  148. {
  149. return x.equal(y.value());
  150. }
  151. private:
  152. template<typename Allocator,typename Q>
  153. void allocator_copy(Allocator& al,const Q& x)
  154. {
  155. allocator_copy(al,x,is_copy_constructible{});
  156. }
  157. template<typename Allocator,typename Q>
  158. void allocator_copy(Allocator& al,const Q& x,std::true_type)
  159. {
  160. std::allocator_traits<Allocator>::construct(al,data(),x);
  161. }
  162. template<typename Allocator,typename Q>
  163. void allocator_copy(Allocator&,const Q&,std::false_type)
  164. {
  165. throw not_copy_constructible{typeid(U)};
  166. }
  167. template<typename Q>
  168. void copy(const Q& x){copy(x,is_copy_constructible{});}
  169. template<typename Q>
  170. void copy(const Q& x,std::true_type)
  171. {
  172. ::new (data()) T(x);
  173. }
  174. template<typename Q>
  175. void copy(const Q&,std::false_type)
  176. {
  177. throw not_copy_constructible{typeid(U)};
  178. }
  179. void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});}
  180. void move_assign(T&& x,std::true_type)
  181. {
  182. value()=std::move(x);
  183. }
  184. void move_assign(T&& x,std::false_type)
  185. {
  186. /* emulated assignment */
  187. static_assert(is_nothrow_move_constructible::value,
  188. "type should be move assignable or nothrow move constructible");
  189. if(data()!=std::addressof(x)){
  190. value().~T();
  191. ::new (data()) T(std::move(x));
  192. }
  193. }
  194. bool equal(const T& x)const{return equal(x,is_equality_comparable{});}
  195. bool equal(const T& x,std::true_type)const
  196. {
  197. return value()==x;
  198. }
  199. bool equal(const T&,std::false_type)const
  200. {
  201. throw not_equality_comparable{typeid(T)};
  202. }
  203. };
  204. } /* namespace poly_collection::detail */
  205. } /* namespace poly_collection */
  206. } /* namespace boost */
  207. #endif