massively improved segment lookup time
This commit is contained in:
parent
3bdf47cb05
commit
c5f6e83938
@ -22,12 +22,11 @@
|
||||
#include <boost/poly_collection/detail/is_final.hpp>
|
||||
#include <boost/poly_collection/detail/newdelete_allocator.hpp>
|
||||
#include <boost/poly_collection/detail/segment.hpp>
|
||||
#include <boost/poly_collection/detail/type_info_map.hpp>
|
||||
#include <boost/poly_collection/exception.hpp>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace boost{
|
||||
@ -40,17 +39,6 @@ namespace poly_collection_impl{
|
||||
|
||||
/* common implementation for all polymorphic collections */
|
||||
|
||||
struct type_info_ptr_hash
|
||||
{
|
||||
std::size_t operator()(const std::type_info* p)const{return p->hash_code();}
|
||||
};
|
||||
|
||||
struct type_info_ptr_equal_to
|
||||
{
|
||||
bool operator()(const std::type_info* p,const std::type_info* q)const
|
||||
{return *p==*q;}
|
||||
};
|
||||
|
||||
template<typename Model,typename Allocator>
|
||||
class poly_collection
|
||||
{
|
||||
@ -127,13 +115,11 @@ class poly_collection
|
||||
template<typename T>
|
||||
using const_segment_iterator=
|
||||
typename segment_type::template const_iterator<T>;
|
||||
using segment_map=std::unordered_map<
|
||||
const std::type_info*,segment_type,
|
||||
type_info_ptr_hash,type_info_ptr_equal_to,
|
||||
using segment_map=type_info_map<
|
||||
segment_type,
|
||||
newdelete_allocator_adaptor<
|
||||
typename std::allocator_traits<Allocator>::template
|
||||
rebind_alloc<std::pair<const std::type_info* const,segment_type>
|
||||
>
|
||||
rebind_alloc<segment_type>
|
||||
>
|
||||
>;
|
||||
using segment_map_allocator_type=typename segment_map::allocator_type;
|
||||
@ -432,13 +418,13 @@ public:
|
||||
(void)seq{
|
||||
0,
|
||||
(map.insert(
|
||||
{&typeid(T),segment_type::template make<T>(get_allocator())}),0)...
|
||||
typeid(T),segment_type::template make<T>(get_allocator())),0)...
|
||||
};
|
||||
}
|
||||
|
||||
bool is_registered(const std::type_info& info)const
|
||||
{
|
||||
return map.find(&info)!=map.end();
|
||||
return map.find(info)!=map.end();
|
||||
}
|
||||
|
||||
template<typename T,enable_if_acceptable<T> =nullptr>
|
||||
@ -981,11 +967,11 @@ private:
|
||||
const_segment_map_iterator get_map_iterator_for(const T& x)
|
||||
{
|
||||
const auto& id=subtypeid(x);
|
||||
auto it=map.find(&id);
|
||||
auto it=map.find(id);
|
||||
if(it!=map.end())return it;
|
||||
else if(id!=typeid(T))throw unregistered_type{id};
|
||||
else return map.insert(
|
||||
{&typeid(T),segment_type::template make<T>(get_allocator())}).first;
|
||||
typeid(T),segment_type::template make<T>(get_allocator())).first;
|
||||
}
|
||||
|
||||
template<
|
||||
@ -995,10 +981,10 @@ private:
|
||||
>
|
||||
const_segment_map_iterator get_map_iterator_for(const T&)
|
||||
{
|
||||
auto it=map.find(&typeid(T));
|
||||
auto it=map.find(typeid(T));
|
||||
if(it!=map.end())return it;
|
||||
else return map.insert(
|
||||
{&typeid(T),segment_type::template make<T>(get_allocator())}).first;
|
||||
typeid(T),segment_type::template make<T>(get_allocator())).first;
|
||||
}
|
||||
|
||||
template<
|
||||
@ -1009,7 +995,7 @@ private:
|
||||
const_segment_map_iterator get_map_iterator_for(const T& x)const
|
||||
{
|
||||
const auto& id=subtypeid(x);
|
||||
auto it=map.find(&id);
|
||||
auto it=map.find(id);
|
||||
if(it!=map.end())return it;
|
||||
else throw unregistered_type{id};
|
||||
}
|
||||
@ -1032,18 +1018,18 @@ private:
|
||||
const T& x,const segment_type& seg)
|
||||
{
|
||||
const auto& id=subtypeid(x);
|
||||
auto it=map.find(&id);
|
||||
auto it=map.find(id);
|
||||
if(it!=map.end())return it;
|
||||
else return map.insert({&id,segment_type::make_from_prototype(seg)}).first;
|
||||
else return map.insert(id,segment_type::make_from_prototype(seg)).first;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const_segment_map_iterator get_map_iterator_for()
|
||||
{
|
||||
auto it=map.find(&typeid(T));
|
||||
auto it=map.find(typeid(T));
|
||||
if(it!=map.end())return it;
|
||||
else return map.insert(
|
||||
{&typeid(T),segment_type::template make<T>(get_allocator())}).first;
|
||||
typeid(T),segment_type::template make<T>(get_allocator())).first;
|
||||
}
|
||||
|
||||
const_segment_map_iterator get_map_iterator_for(const std::type_info& info)
|
||||
@ -1055,7 +1041,7 @@ private:
|
||||
const_segment_map_iterator get_map_iterator_for(
|
||||
const std::type_info& info)const
|
||||
{
|
||||
auto it=map.find(&info);
|
||||
auto it=map.find(info);
|
||||
if(it!=map.end())return it;
|
||||
else throw unregistered_type{info};
|
||||
}
|
||||
@ -1157,7 +1143,7 @@ bool operator==(
|
||||
const auto &mapx=x.map,&mapy=y.map;
|
||||
for(const auto& p:mapx){
|
||||
auto ss=p.second.size();
|
||||
auto it=mapy.find(p.first);
|
||||
auto it=mapy.find(*p.first);
|
||||
if(it==mapy.end()?ss!=0:p.second!=it->second)return false;
|
||||
s+=ss;
|
||||
}
|
||||
|
170
include/boost/poly_collection/detail/type_info_map.hpp
Normal file
170
include/boost/poly_collection/detail/type_info_map.hpp
Normal file
@ -0,0 +1,170 @@
|
||||
/* 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){
|
||||
map=x.map;
|
||||
cache=cache_type{x.cache.get_allocator()};
|
||||
build_cache(x.cache);
|
||||
}
|
||||
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
|
Loading…
Reference in New Issue
Block a user