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
25namespace boost{
26
27namespace poly_collection{
28
29namespace 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
41struct type_info_ptr_hash
42{
43 std::size_t operator()(const std::type_info* p)const noexcept
44 {return p->hash_code();}
45};
46
47struct 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
54template<typename T,typename Allocator>
55class 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
64public:
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> insert(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
174private:
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
246template<typename T,typename Allocator>
247void 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

source code of boost/libs/poly_collection/include/boost/poly_collection/detail/type_info_map.hpp