type_info_map.hpp 6.9 KB

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