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