Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 08:46:20

0001 /* Copyright 2016-2024 Joaquin M Lopez Munoz.
0002  * Distributed under the Boost Software License, Version 1.0.
0003  * (See accompanying file LICENSE_1_0.txt or copy at
0004  * http://www.boost.org/LICENSE_1_0.txt)
0005  *
0006  * See http://www.boost.org/libs/poly_collection for library home page.
0007  */
0008 
0009 #ifndef BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP
0010 #define BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP
0011 
0012 #if defined(_MSC_VER)
0013 #pragma once
0014 #endif
0015 
0016 #include <boost/poly_collection/detail/is_constructible.hpp>
0017 #include <boost/poly_collection/detail/is_equality_comparable.hpp>
0018 #include <boost/poly_collection/detail/is_nothrow_eq_comparable.hpp>
0019 #include <boost/poly_collection/exception.hpp>
0020 #include <new>
0021 #include <memory>
0022 #include <type_traits>
0023 #include <utility>
0024 
0025 namespace boost{
0026 
0027 namespace poly_collection{
0028 
0029 namespace detail{
0030 
0031 /* Segments of a poly_collection maintain vectors of value_holder<T>
0032  * rather than directly T. This serves several purposes:
0033  *  - value_holder<T> is copy constructible and equality comparable even if T
0034  *    is not: executing the corresponding op results in a reporting exception
0035  *    being thrown. This allows the segment to offer its full virtual
0036  *    interface regardless of the properties of the concrete class contained.
0037  *  - value_holder<T> emulates move assignment when T is not move assignable
0038  *    (nothrow move constructibility required); this happens most notably with
0039  *    lambda functions, whose assignment operator is deleted by standard
0040  *    mandate [expr.prim.lambda]/20 even if the compiler generated one would
0041  *    work (capture by value).
0042  *  - value_holder ctors accept a first allocator arg passed by
0043  *    boost::poly_collection::detail::allocator_adaptor, for purposes
0044  *    explained there.
0045  *
0046  * A pointer to value_holder_base<T> can be reinterpret_cast'ed to T*.
0047  * Emplacing is explicitly signalled with value_holder_emplacing_ctor to
0048  * protect us from greedy T's constructible from anything (like
0049  * boost::type_erasure::any).
0050  * 
0051  * If specified, the second arg in value_holder<T,U> is a type
0052  * from where T will be copy/move constructed, rather than T itself.
0053  * T must be (nothrow) copy/move constructible iff it is (nothrow)
0054  * constructible from const U&/U&&. This serves the use case where a segment
0055  * for U does not store Us but objects of some other type (=T) constructed
0056  * from them.
0057  */
0058 
0059 struct value_holder_emplacing_ctor_t{};
0060 constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor=
0061   value_holder_emplacing_ctor_t();
0062 
0063 template<typename T>
0064 class value_holder_base
0065 {
0066 protected:
0067   alignas(T) unsigned char s[sizeof(T)];
0068 };
0069 
0070 template<typename T,typename U=T>
0071 class value_holder:public value_holder_base<T>
0072 {
0073   template<typename Q>
0074   using enable_if_not_emplacing_ctor_t=typename std::enable_if<
0075     !std::is_same<
0076       typename std::decay<Q>::type,value_holder_emplacing_ctor_t
0077     >::value
0078   >::type*;
0079 
0080   using is_nothrow_move_constructible=std::is_nothrow_move_constructible<U>;
0081   using is_copy_constructible=std::is_copy_constructible<U>;
0082   using is_nothrow_copy_constructible=std::is_nothrow_copy_constructible<U>;
0083   using is_move_assignable=std::is_move_assignable<U>;
0084   using is_nothrow_move_assignable=std::is_nothrow_move_assignable<U>;
0085   using is_equality_comparable=detail::is_equality_comparable<U>;
0086   using is_nothrow_equality_comparable=detail::is_nothrow_equality_comparable<U>;
0087 
0088   T*       data()noexcept{return reinterpret_cast<T*>(&this->s);}
0089   const T* data()const noexcept
0090                 {return reinterpret_cast<const T*>(&this->s);}
0091 
0092   T&       value()noexcept{return *static_cast<T*>(data());}
0093   const T& value()const noexcept{return *static_cast<const T*>(data());}
0094 
0095 public:
0096   template<
0097     typename Allocator,
0098     enable_if_not_emplacing_ctor_t<Allocator> =nullptr
0099   >
0100   value_holder(Allocator& al,const U& x)
0101     noexcept(is_nothrow_copy_constructible::value)
0102     {allocator_copy(al,x);}
0103   template<
0104     typename Allocator,
0105     enable_if_not_emplacing_ctor_t<Allocator> =nullptr
0106   >
0107   value_holder(Allocator& al,U&& x)
0108     noexcept(is_nothrow_move_constructible::value)
0109     {std::allocator_traits<Allocator>::construct(al,data(),std::move(x));}
0110   template<
0111     typename Allocator,typename... Args,
0112     enable_if_not_emplacing_ctor_t<Allocator> =nullptr
0113   >
0114   value_holder(Allocator& al,value_holder_emplacing_ctor_t,Args&&... args)
0115     {std::allocator_traits<Allocator>::construct(
0116       al,data(),std::forward<Args>(args)...);}
0117   template<
0118     typename Allocator,
0119     enable_if_not_emplacing_ctor_t<Allocator> =nullptr
0120   >
0121   value_holder(Allocator& al,const value_holder& x)
0122     noexcept(is_nothrow_copy_constructible::value)
0123     {allocator_copy(al,x.value());}
0124   template<
0125     typename Allocator,
0126     enable_if_not_emplacing_ctor_t<Allocator> =nullptr
0127   >
0128   value_holder(Allocator& al,value_holder&& x)
0129     noexcept(is_nothrow_move_constructible::value)
0130     {std::allocator_traits<Allocator>::construct(
0131       al,data(),std::move(x.value()));}
0132 
0133   /* stdlib implementations in current use are notoriously lacking at
0134    * complying with [container.requirements.general]/3, so we keep the
0135    * following to make their life easier.
0136    */
0137 
0138   value_holder(const U& x)
0139     noexcept(is_nothrow_copy_constructible::value)
0140     {copy(x);}
0141   value_holder(U&& x)
0142     noexcept(is_nothrow_move_constructible::value)
0143     {::new ((void*)data()) T(std::move(x));}
0144   template<typename... Args>
0145   value_holder(value_holder_emplacing_ctor_t,Args&&... args)
0146     {::new ((void*)data()) T(std::forward<Args>(args)...);}
0147   value_holder(const value_holder& x)
0148     noexcept(is_nothrow_copy_constructible::value)
0149     {copy(x.value());}
0150   value_holder(value_holder&& x)
0151     noexcept(is_nothrow_move_constructible::value)
0152     {::new ((void*)data()) T(std::move(x.value()));}
0153  
0154   value_holder& operator=(const value_holder& x)=delete;
0155   value_holder& operator=(value_holder&& x)
0156     noexcept(is_nothrow_move_assignable::value||!is_move_assignable::value)
0157     /* if 2nd clause: nothrow move constructibility required */
0158   {
0159     move_assign(std::move(x.value()));
0160     return *this;
0161   }
0162 
0163   ~value_holder()noexcept{value().~T();}
0164 
0165   friend bool operator==(const value_holder& x,const value_holder& y)
0166     noexcept(is_nothrow_equality_comparable::value)
0167   {
0168     return x.equal(y.value());
0169   }
0170 
0171 private:
0172   template<typename Allocator,typename Q>
0173   void allocator_copy(Allocator& al,const Q& x)
0174   {
0175     allocator_copy(al,x,is_copy_constructible{});
0176   }
0177 
0178   template<typename Allocator,typename Q>
0179   void allocator_copy(Allocator& al,const Q& x,std::true_type)
0180   {
0181     std::allocator_traits<Allocator>::construct(al,data(),x);
0182   }
0183 
0184   template<typename Allocator,typename Q>
0185   void allocator_copy(Allocator&,const Q&,std::false_type)
0186   {
0187     throw not_copy_constructible{typeid(U)};
0188   }
0189 
0190   template<typename Q>
0191   void copy(const Q& x){copy(x,is_copy_constructible{});}
0192 
0193   template<typename Q>
0194   void copy(const Q& x,std::true_type)
0195   {
0196     ::new (data()) T(x);
0197   }
0198 
0199   template<typename Q>
0200   void copy(const Q&,std::false_type)
0201   {
0202     throw not_copy_constructible{typeid(U)};
0203   }
0204 
0205   void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});}
0206 
0207   void move_assign(T&& x,std::true_type)
0208   {
0209     value()=std::move(x);    
0210   }
0211 
0212   void move_assign(T&& x,std::false_type)
0213   {
0214     /* emulated assignment */
0215 
0216     static_assert(is_nothrow_move_constructible::value,
0217       "type should be move assignable or nothrow move constructible");
0218 
0219     if(data()!=std::addressof(x)){
0220       value().~T();
0221       ::new (data()) T(std::move(x));
0222     }
0223   }
0224 
0225   bool equal(const T& x)const{return equal(x,is_equality_comparable{});}
0226 
0227   bool equal(const T& x,std::true_type)const
0228   {
0229     return value()==x;
0230   }
0231 
0232   bool equal(const T&,std::false_type)const
0233   {
0234     throw not_equality_comparable{typeid(T)};
0235   }
0236 };
0237 
0238 } /* namespace poly_collection::detail */
0239 
0240 } /* namespace poly_collection */
0241 
0242 } /* namespace boost */
0243 
0244 #endif