| 1 | /* Copyright 2016-2020 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_TYPE_INFO_MAP_HPP |
| 10 | #define BOOST_POLY_COLLECTION_DETAIL_TYPE_INFO_MAP_HPP |
| 11 | |
| 12 | #if defined(_MSC_VER) |
| 13 | #pragma once |
| 14 | #endif |
| 15 | |
| 16 | #include <boost/detail/workaround.hpp> |
| 17 | #include <functional> |
| 18 | #include <iterator> |
| 19 | #include <memory> |
| 20 | #include <type_traits> |
| 21 | #include <typeinfo> |
| 22 | #include <unordered_map> |
| 23 | #include <utility> |
| 24 | |
| 25 | namespace boost{ |
| 26 | |
| 27 | namespace poly_collection{ |
| 28 | |
| 29 | namespace detail{ |
| 30 | |
| 31 | /* To cope with dynamic modules/libs, the standard allows for different |
| 32 | * std::type_info instances to describe the same type, which implies that |
| 33 | * std::type_info::operator== and std::type_info::hash_code are costly |
| 34 | * operations typically relying on the stored type name. |
| 35 | * type_info_ptr_hash<T> behaves roughly as a |
| 36 | * std::unordered_map<std::type_index,T> but maintains an internal cache of |
| 37 | * passed std::type_info instances so that lookup is performed (when there's a |
| 38 | * cache hit) without invoking std::type_info equality and hashing ops. |
| 39 | */ |
| 40 | |
| 41 | struct type_info_ptr_hash |
| 42 | { |
| 43 | std::size_t operator()(const std::type_info* p)const noexcept |
| 44 | {return p->hash_code();} |
| 45 | }; |
| 46 | |
| 47 | struct type_info_ptr_equal_to |
| 48 | { |
| 49 | bool operator()( |
| 50 | const std::type_info* p,const std::type_info* q)const noexcept |
| 51 | {return *p==*q;} |
| 52 | }; |
| 53 | |
| 54 | template<typename T,typename Allocator> |
| 55 | class type_info_map |
| 56 | { |
| 57 | using map_type=std::unordered_map< |
| 58 | const std::type_info*,T, |
| 59 | type_info_ptr_hash,type_info_ptr_equal_to, |
| 60 | typename std::allocator_traits<Allocator>::template |
| 61 | rebind_alloc<std::pair<const std::type_info* const,T>> |
| 62 | >; |
| 63 | |
| 64 | public: |
| 65 | using key_type=std::type_info; |
| 66 | using mapped_type=T; |
| 67 | using value_type=typename map_type::value_type; |
| 68 | using allocator_type=typename map_type::allocator_type; |
| 69 | using iterator=typename map_type::iterator; |
| 70 | using const_iterator=typename map_type::const_iterator; |
| 71 | |
| 72 | type_info_map()=default; |
| 73 | type_info_map(const type_info_map& x): |
| 74 | map{x.map}, |
| 75 | cache{make<cache_type>(std::allocator_traits<cache_allocator_type>:: |
| 76 | select_on_container_copy_construction(x.cache.get_allocator()))} |
| 77 | {build_cache(x: x.cache);} |
| 78 | type_info_map(type_info_map&& x)=default; |
| 79 | type_info_map(const allocator_type& al): |
| 80 | map{make<map_type>(al)},cache{make<cache_type>(al)}{} |
| 81 | type_info_map(const type_info_map& x,const allocator_type& al): |
| 82 | map{make(x.map,al)},cache{make<cache_type>(al)} |
| 83 | {build_cache(x: x.cache);} |
| 84 | type_info_map(type_info_map&& x,const allocator_type& al): |
| 85 | map{make(std::move(x.map),al)}, |
| 86 | cache{ |
| 87 | al==allocator_type{x.map.get_allocator()}&&x.map.empty()? |
| 88 | make(std::move(x.cache),al): |
| 89 | make<cache_type>(al) |
| 90 | } |
| 91 | { |
| 92 | if(!(al==allocator_type{x.map.get_allocator()}&&x.map.empty())){ |
| 93 | build_cache(x: x.cache); |
| 94 | } |
| 95 | x.map.clear(); |
| 96 | x.cache.clear(); |
| 97 | } |
| 98 | |
| 99 | type_info_map& operator=(const type_info_map& x) |
| 100 | { |
| 101 | if(this!=&x)try{ |
| 102 | map=x.map; |
| 103 | cache=make<cache_type>(map.get_allocator()); |
| 104 | build_cache(x: x.cache); |
| 105 | } |
| 106 | catch(...){ |
| 107 | map.clear(); |
| 108 | cache.clear(); |
| 109 | throw; |
| 110 | } |
| 111 | return *this; |
| 112 | } |
| 113 | |
| 114 | type_info_map& operator=(type_info_map&& x) |
| 115 | { |
| 116 | if(this!=&x)try{ |
| 117 | map=std::move(x.map); |
| 118 | if(map.get_allocator()==x.map.get_allocator()){ |
| 119 | cache=std::move(x.cache); |
| 120 | } |
| 121 | else{ |
| 122 | cache=make<cache_type>(map.get_allocator()); |
| 123 | build_cache(x: x.cache); |
| 124 | x.cache.clear(); |
| 125 | } |
| 126 | } |
| 127 | catch(...){ |
| 128 | map.clear(); |
| 129 | cache.clear(); |
| 130 | x.map.clear(); |
| 131 | x.cache.clear(); |
| 132 | throw; |
| 133 | } |
| 134 | return *this; |
| 135 | } |
| 136 | |
| 137 | allocator_type get_allocator()const noexcept{return map.get_allocator();} |
| 138 | |
| 139 | iterator begin()noexcept{return map.begin();} |
| 140 | iterator end()noexcept{return map.end();} |
| 141 | const_iterator begin()const noexcept{return map.begin();} |
| 142 | const_iterator end()const noexcept{return map.end();} |
| 143 | const_iterator cbegin()const noexcept{return map.cbegin();} |
| 144 | const_iterator cend()const noexcept{return map.cend();} |
| 145 | |
| 146 | iterator find(const key_type& key) |
| 147 | { |
| 148 | auto cit=cache.find(&key); |
| 149 | if(cit!=cache.end())return cit->second; |
| 150 | auto mit=map.find(&key); |
| 151 | if(mit!=map.end())cache.insert({&key,mit}); |
| 152 | return mit; |
| 153 | } |
| 154 | |
| 155 | const_iterator find(const key_type& key)const |
| 156 | { |
| 157 | auto cit=cache.find(&key); |
| 158 | if(cit!=cache.end())return cit->second; |
| 159 | return map.find(&key); |
| 160 | } |
| 161 | |
| 162 | template<typename P> |
| 163 | std::pair<iterator,bool> (const key_type& key,P&& x) |
| 164 | { |
| 165 | auto c=map.bucket_count(); |
| 166 | auto p=map.emplace(&key,std::forward<P>(x)); |
| 167 | if(map.bucket_count()!=c)rebuild_cache(); |
| 168 | cache.insert({&key,p.first}); |
| 169 | return p; |
| 170 | } |
| 171 | |
| 172 | void swap(type_info_map& x){map.swap(x.map);cache.swap(x.cache);} |
| 173 | |
| 174 | private: |
| 175 | using cache_type=std::unordered_map< |
| 176 | const std::type_info*,iterator, |
| 177 | std::hash<const std::type_info*>,std::equal_to<const std::type_info*>, |
| 178 | typename std::allocator_traits<Allocator>::template |
| 179 | rebind_alloc<std::pair<const std::type_info* const,iterator>> |
| 180 | >; |
| 181 | using cache_allocator_type=typename cache_type::allocator_type; |
| 182 | |
| 183 | #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900) |
| 184 | /* std::unordered_map(const allocator_type&), |
| 185 | * std::unordered_map(const unordered_map&,const allocator_type&) and |
| 186 | * std::unordered_map(unordered_map&&,const allocator_type&) not available. |
| 187 | */ |
| 188 | |
| 189 | template<typename UnorderedMap> |
| 190 | static UnorderedMap make(const typename UnorderedMap::allocator_type& al) |
| 191 | { |
| 192 | return UnorderedMap{ |
| 193 | 10,typename UnorderedMap::hasher{},typename UnorderedMap::key_equal{},al |
| 194 | }; |
| 195 | } |
| 196 | |
| 197 | template<typename UnorderedMap> |
| 198 | static typename std::decay<UnorderedMap>::type make( |
| 199 | UnorderedMap&& x, |
| 200 | const typename std::decay<UnorderedMap>::type::allocator_type& al) |
| 201 | { |
| 202 | using RawUnorderedMap=typename std::decay<UnorderedMap>::type; |
| 203 | using iterator=typename std::conditional< |
| 204 | !std::is_lvalue_reference<UnorderedMap>::value&& |
| 205 | !std::is_const<UnorderedMap>::value, |
| 206 | std::move_iterator<typename RawUnorderedMap::iterator>, |
| 207 | typename RawUnorderedMap::const_iterator |
| 208 | >::type; |
| 209 | |
| 210 | return RawUnorderedMap{ |
| 211 | iterator{x.begin()},iterator{x.end()},0, |
| 212 | typename RawUnorderedMap::hasher{},typename RawUnorderedMap::key_equal{}, |
| 213 | al |
| 214 | }; |
| 215 | } |
| 216 | #else |
| 217 | template<typename UnorderedMap> |
| 218 | static UnorderedMap make(const typename UnorderedMap::allocator_type& al) |
| 219 | { |
| 220 | return UnorderedMap{al}; |
| 221 | } |
| 222 | |
| 223 | template<typename UnorderedMap> |
| 224 | static typename std::decay<UnorderedMap>::type make( |
| 225 | UnorderedMap&& x, |
| 226 | const typename std::decay<UnorderedMap>::type::allocator_type& al) |
| 227 | { |
| 228 | return {std::forward<UnorderedMap>(x),al}; |
| 229 | } |
| 230 | #endif |
| 231 | |
| 232 | void build_cache(const cache_type& x) |
| 233 | { |
| 234 | for(const auto& p:x)cache.insert({p.first,map.find(p.first)}); |
| 235 | } |
| 236 | |
| 237 | void rebuild_cache() |
| 238 | { |
| 239 | for(auto& p:cache)p.second=map.find(p.first); |
| 240 | } |
| 241 | |
| 242 | map_type map; |
| 243 | cache_type cache; |
| 244 | }; |
| 245 | |
| 246 | template<typename T,typename Allocator> |
| 247 | void swap(type_info_map<T,Allocator>& x,type_info_map<T,Allocator>& y) |
| 248 | { |
| 249 | x.swap(y); |
| 250 | } |
| 251 | |
| 252 | } /* namespace poly_collection::detail */ |
| 253 | |
| 254 | } /* namespace poly_collection */ |
| 255 | |
| 256 | } /* namespace boost */ |
| 257 | |
| 258 | #endif |
| 259 | |