diff options
Diffstat (limited to 'boost/poly_collection/detail/type_info_map.hpp')
-rw-r--r-- | boost/poly_collection/detail/type_info_map.hpp | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/boost/poly_collection/detail/type_info_map.hpp b/boost/poly_collection/detail/type_info_map.hpp new file mode 100644 index 0000000000..289af10adb --- /dev/null +++ b/boost/poly_collection/detail/type_info_map.hpp @@ -0,0 +1,169 @@ +/* Copyright 2016-2017 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_TYPE_INFO_MAP_HPP +#define BOOST_POLY_COLLECTION_DETAIL_TYPE_INFO_MAP_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include <boost/poly_collection/detail/newdelete_allocator.hpp> +#include <functional> +#include <typeinfo> +#include <unordered_map> +#include <utility> + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +/* To cope with dynamic modules/libs, the standard allows for different + * std::type_info instances to describe the same type, which implies that + * std::type_info::operator== and std::type_info::hash_code are costly + * operations typically relying on the stored type name. + * type_info_ptr_hash<T> behaves roughly as a + * std::unordered_map<std::type_index,T> but maintains an internal cache of + * passed std::type_info instances so that lookup is performed (when there's a + * cache hit) without invoking std::type_info equality and hashing ops. + */ + +struct type_info_ptr_hash +{ + std::size_t operator()(const std::type_info* p)const noexcept + {return p->hash_code();} +}; + +struct type_info_ptr_equal_to +{ + bool operator()( + const std::type_info* p,const std::type_info* q)const noexcept + {return *p==*q;} +}; + + +template<typename T,typename Allocator> +class type_info_map +{ + using map_type=std::unordered_map< + const std::type_info*,T, + type_info_ptr_hash,type_info_ptr_equal_to, + typename std::allocator_traits<Allocator>::template + rebind_alloc<std::pair<const std::type_info* const,T>> + >; + +public: + using key_type=std::type_info; + using mapped_type=T; + using value_type=typename map_type::value_type; + using allocator_type=typename map_type::allocator_type; + using iterator=typename map_type::iterator; + using const_iterator=typename map_type::const_iterator; + + type_info_map()=default; + type_info_map(const type_info_map& x): + map{x.map},cache{x.cache.get_allocator()}{build_cache(x.cache);} + type_info_map(type_info_map&& x)=default; + type_info_map(const allocator_type& al): + map{al},cache{cache_allocator_type{al}}{} + type_info_map(const type_info_map& x,const allocator_type& al): + map{x.map,al},cache{cache_allocator_type{al}}{build_cache(x.cache);} + type_info_map(type_info_map&& x,const allocator_type& al): + map{std::move(x.map),al},cache{cache_allocator_type{al}} + { + if(al==x.map.get_allocator()&& + cache_allocator_type{al}==x.cache.get_allocator()){ + cache=std::move(x.cache); + } + else{ + build_cache(x.cache); + x.cache.clear(); + } + } + + type_info_map& operator=(const type_info_map& x) + { + if(this!=&x){ + type_info_map c{x}; + swap(c); + } + return *this; + } + + type_info_map& operator=(type_info_map&& x)=default; + + allocator_type get_allocator()const noexcept{return map.get_allocator();} + + iterator begin()noexcept{return map.begin();} + iterator end()noexcept{return map.end();} + const_iterator begin()const noexcept{return map.begin();} + const_iterator end()const noexcept{return map.end();} + const_iterator cbegin()const noexcept{return map.cbegin();} + const_iterator cend()const noexcept{return map.cend();} + + iterator find(const key_type& key) + { + auto cit=cache.find(&key); + if(cit!=cache.end())return cit->second; + auto mit=map.find(&key); + if(mit!=map.end())cache.insert({&key,mit}); + return mit; + } + + const_iterator find(const key_type& key)const + { + auto cit=cache.find(&key); + if(cit!=cache.end())return cit->second; + return map.find(&key); + } + + template<typename P> + std::pair<iterator,bool> insert(const key_type& key,P&& x) + { + auto p=map.insert({&key,std::forward<P>(x)}); + cache.insert({&key,p.first}); + return p; + } + + void swap(type_info_map& x){map.swap(x.map);cache.swap(x.cache);} + +private: + using cache_type=std::unordered_map< + const std::type_info*,iterator, + std::hash<const std::type_info*>,std::equal_to<const std::type_info*>, + newdelete_allocator_adaptor< + typename std::allocator_traits<Allocator>::template + rebind_alloc<std::pair<const std::type_info* const,iterator>> + > + >; + using cache_allocator_type=typename cache_type::allocator_type; + + void build_cache(const cache_type& x) + { + for(const auto& p:x)cache.insert({p.first,map.find(p.first)}); + } + + map_type map; + cache_type cache; +}; + +template<typename T,typename Allocator> +void swap(type_info_map<T,Allocator>& x,type_info_map<T,Allocator>& y) +{ + x.swap(y); +} + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif |