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