improved lookup efficiency in the event of implicit conversion to key_type

This commit is contained in:
joaquintides 2014-11-01 15:24:31 +01:00
parent 87ff899ceb
commit 79541fce51
5 changed files with 460 additions and 36 deletions

View File

@ -0,0 +1,103 @@
/* Copyright 2003-2014 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/multi_index for library home page.
*/
#ifndef BOOST_MULTI_INDEX_DETAIL_IS_TRANSPARENT_HPP
#define BOOST_MULTI_INDEX_DETAIL_IS_TRANSPARENT_HPP
#if defined(_MSC_VER)
#pragma once
#endif
#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/intrinsics.hpp>
namespace boost{
namespace multi_index{
namespace detail{
/* Metafunction that checks if f(arg,arg2) executes without argument type
* conversion. By default (i.e. when it cannot be determined) it evaluates to
* true.
*/
template<typename F,typename Arg1,typename Arg2,typename=void>
struct is_transparent:mpl::true_{};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#if !defined(BOOST_NO_SFINAE)&& \
!defined(BOOST_NO_CXX11_DECLTYPE)&& \
defined(BOOST_IS_FINAL)
#include <boost/mpl/and.hpp>
#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/type_traits/is_final.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/declval.hpp>
#include <boost/utility/enable_if.hpp>
namespace boost{
namespace multi_index{
namespace detail{
struct not_is_transparent_result_type{};
template<typename F,typename Arg1,typename Arg2>
struct is_transparent_class_helper:F
{
using F::operator();
template<typename T,typename Q>
not_is_transparent_result_type operator()(T,Q)const;
};
template<typename F,typename Arg1,typename Arg2,typename=void>
struct is_transparent_class:mpl::true_{};
template<typename F,typename Arg1,typename Arg2>
struct is_transparent_class<
F,Arg1,Arg2,
typename enable_if<
is_same<
decltype(
declval<const is_transparent_class_helper<F,Arg1,Arg2> >()(
declval<const Arg1&>(),declval<const Arg2&>())
),
not_is_transparent_result_type
>
>::type
>:mpl::false_{};
template<typename F,typename Arg1,typename Arg2>
struct is_transparent<
F,Arg1,Arg2,
typename enable_if<
mpl::and_<
is_class<F>,
mpl::not_<is_final<F> >
>
>::type
>:is_transparent_class<F,Arg1,Arg2>{};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#endif
#endif

View File

@ -1,4 +1,4 @@
/* Copyright 2003-2013 Joaquin M Lopez Munoz.
/* Copyright 2003-2014 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)
@ -41,6 +41,8 @@
#endif
#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
#include <boost/mpl/and.hpp>
#include <boost/multi_index/detail/promotes_arg.hpp>
#include <utility>
namespace boost{
@ -51,6 +53,9 @@ namespace detail{
/* Common code for index memfuns having templatized and
* non-templatized versions.
* Implementation note: When CompatibleKey is consistently promoted to
* KeyFromValue::result_type for comparison, the promotion is made once in
* advance to increase efficiency.
*/
template<
@ -60,6 +65,35 @@ template<
inline Node* ordered_index_find(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp)
{
typedef typename KeyFromValue::result_type key_type;
return ordered_index_find(
top,y,key,x,comp,
mpl::and_<
promotes_1st_arg<CompatibleCompare,CompatibleKey,key_type>,
promotes_2nd_arg<CompatibleCompare,key_type,CompatibleKey>>());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleCompare
>
inline Node* ordered_index_find(
Node* top,Node* y,const KeyFromValue& key,
const BOOST_DEDUCED_TYPENAME KeyFromValue::result_type& x,
const CompatibleCompare& comp,mpl::true_)
{
return ordered_index_find(top,y,key,x,comp,mpl::false_());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleKey,typename CompatibleCompare
>
inline Node* ordered_index_find(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp,mpl::false_)
{
Node* y0=y;
@ -81,6 +115,33 @@ template<
inline Node* ordered_index_lower_bound(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp)
{
typedef typename KeyFromValue::result_type key_type;
return ordered_index_lower_bound(
top,y,key,x,comp,
promotes_2nd_arg<CompatibleCompare,key_type,CompatibleKey>());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleCompare
>
inline Node* ordered_index_lower_bound(
Node* top,Node* y,const KeyFromValue& key,
const BOOST_DEDUCED_TYPENAME KeyFromValue::result_type& x,
const CompatibleCompare& comp,mpl::true_)
{
return ordered_index_lower_bound(top,y,key,x,comp,mpl::false_());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleKey,typename CompatibleCompare
>
inline Node* ordered_index_lower_bound(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp,mpl::false_)
{
while(top){
if(!comp(key(top->value()),x)){
@ -100,6 +161,33 @@ template<
inline Node* ordered_index_upper_bound(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp)
{
typedef typename KeyFromValue::result_type key_type;
return ordered_index_upper_bound(
top,y,key,x,comp,
promotes_1st_arg<CompatibleCompare,CompatibleKey,key_type>());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleCompare
>
inline Node* ordered_index_upper_bound(
Node* top,Node* y,const KeyFromValue& key,
const BOOST_DEDUCED_TYPENAME KeyFromValue::result_type& x,
const CompatibleCompare& comp,mpl::true_)
{
return ordered_index_upper_bound(top,y,key,x,comp,mpl::false_());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleKey,typename CompatibleCompare
>
inline Node* ordered_index_upper_bound(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp,mpl::false_)
{
while(top){
if(comp(x,key(top->value()))){
@ -119,6 +207,35 @@ template<
inline std::pair<Node*,Node*> ordered_index_equal_range(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp)
{
typedef typename KeyFromValue::result_type key_type;
return ordered_index_equal_range(
top,y,key,x,comp,
mpl::and_<
promotes_1st_arg<CompatibleCompare,CompatibleKey,key_type>,
promotes_2nd_arg<CompatibleCompare,key_type,CompatibleKey>>());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleCompare
>
inline std::pair<Node*,Node*> ordered_index_equal_range(
Node* top,Node* y,const KeyFromValue& key,
const BOOST_DEDUCED_TYPENAME KeyFromValue::result_type& x,
const CompatibleCompare& comp,mpl::true_)
{
return ordered_index_equal_range(top,y,key,x,comp,mpl::false_());
}
template<
typename Node,typename KeyFromValue,
typename CompatibleKey,typename CompatibleCompare
>
inline std::pair<Node*,Node*> ordered_index_equal_range(
Node* top,Node* y,const KeyFromValue& key,const CompatibleKey& x,
const CompatibleCompare& comp,mpl::false_)
{
while(top){
if(comp(key(top->value()),x)){
@ -130,8 +247,10 @@ inline std::pair<Node*,Node*> ordered_index_equal_range(
}
else{
return std::pair<Node*,Node*>(
ordered_index_lower_bound(Node::from_impl(top->left()),top,key,x,comp),
ordered_index_upper_bound(Node::from_impl(top->right()),y,key,x,comp));
ordered_index_lower_bound(
Node::from_impl(top->left()),top,key,x,comp,mpl::false_()),
ordered_index_upper_bound(
Node::from_impl(top->right()),y,key,x,comp,mpl::false_()));
}
}

View File

@ -0,0 +1,83 @@
/* Copyright 2003-2014 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/multi_index for library home page.
*/
#ifndef BOOST_MULTI_INDEX_DETAIL_PROMOTES_ARG_HPP
#define BOOST_MULTI_INDEX_DETAIL_PROMOTES_ARG_HPP
#if defined(_MSC_VER)
#pragma once
#endif
#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/intrinsics.hpp>
/* Metafunctions to check if f(arg1,arg2) promotes either arg1 to the type of
* arg2 or viceversa. By default, (i.e. if it cannot be determined), no
* promotion is assumed.
*/
#if !defined(BOOST_IS_CONVERTIBLE)
namespace boost{
namespace multi_index{
namespace detail{
template<typename F,typename Arg1,typename Arg2>
struct promotes_1st_arg:mpl::false_{};
template<typename F,typename Arg1,typename Arg2>
struct promotes_2nd_arg:mpl::false_{};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#else
#include <boost/mpl/and.hpp>
#include <boost/mpl/not.hpp>
#include <boost/multi_index/detail/is_transparent.hpp>
#include <boost/type_traits/is_convertible.hpp>
namespace boost{
namespace multi_index{
namespace detail{
template<typename F,typename Arg1,typename Arg2>
struct promotes_1st_arg:
mpl::and_<
mpl::not_<is_transparent<F,Arg1,Arg2> >,
is_convertible<const Arg1,Arg2>,
is_transparent<F,Arg2,Arg2>
>
{};
template<typename F,typename Arg1,typename Arg2>
struct promotes_2nd_arg:
mpl::and_<
mpl::not_<is_transparent<F,Arg1,Arg2> >,
is_convertible<const Arg2,Arg1>,
is_transparent<F,Arg1,Arg1>
>
{};
} /* namespace multi_index::detail */
} /* namespace multi_index */
} /* namespace boost */
#endif /* defined(BOOST_IS_CONVERTIBLE) */
#endif

View File

@ -32,6 +32,7 @@
#include <boost/multi_index/detail/hash_index_iterator.hpp>
#include <boost/multi_index/detail/index_node_base.hpp>
#include <boost/multi_index/detail/modify_key_adaptor.hpp>
#include <boost/multi_index/detail/promotes_arg.hpp>
#include <boost/multi_index/detail/safe_mode.hpp>
#include <boost/multi_index/detail/scope_guard.hpp>
#include <boost/multi_index/detail/vartempl_support.hpp>
@ -459,6 +460,11 @@ public:
* type as iterator.
*/
/* Implementation note: When CompatibleKey is consistently promoted to
* KeyFromValue::result_type for equality comparison, the promotion is made
* once in advance to increase efficiency.
*/
template<typename CompatibleKey>
iterator find(const CompatibleKey& k)const
{
@ -472,14 +478,8 @@ public:
const CompatibleKey& k,
const CompatibleHash& hash,const CompatiblePred& eq)const
{
std::size_t buc=buckets.position(hash(k));
for(node_impl_pointer x=buckets.at(buc)->prior();
x!=node_impl_pointer(0);x=node_alg::next_to_inspect(x)){
if(eq(k,key(node_type::from_impl(x)->value()))){
return make_iterator(node_type::from_impl(x));
}
}
return end();
return find(
k,hash,eq,promotes_1st_arg<CompatiblePred,CompatibleKey,key_type>());
}
template<typename CompatibleKey>
@ -495,20 +495,8 @@ public:
const CompatibleKey& k,
const CompatibleHash& hash,const CompatiblePred& eq)const
{
std::size_t buc=buckets.position(hash(k));
for(node_impl_pointer x=buckets.at(buc)->prior();
x!=node_impl_pointer(0);x=node_alg::next_to_inspect(x)){
if(eq(k,key(node_type::from_impl(x)->value()))){
size_type res=0;
node_impl_pointer y=end_of_range(x);
do{
++res;
x=node_alg::after(x);
}while(x!=y);
return res;
}
}
return 0;
return count(
k,hash,eq,promotes_1st_arg<CompatiblePred,CompatibleKey,key_type>());
}
template<typename CompatibleKey>
@ -524,16 +512,8 @@ public:
const CompatibleKey& k,
const CompatibleHash& hash,const CompatiblePred& eq)const
{
std::size_t buc=buckets.position(hash(k));
for(node_impl_pointer x=buckets.at(buc)->prior();
x!=node_impl_pointer(0);x=node_alg::next_to_inspect(x)){
if(eq(k,key(node_type::from_impl(x)->value()))){
return std::pair<iterator,iterator>(
make_iterator(node_type::from_impl(x)),
make_iterator(node_type::from_impl(end_of_range(x))));
}
}
return std::pair<iterator,iterator>(end(),end());
return equal_range(
k,hash,eq,promotes_1st_arg<CompatiblePred,CompatibleKey,key_type>());
}
/* bucket interface */
@ -1527,6 +1507,95 @@ private:
return make_iterator(p.first);
}
template<
typename CompatibleHash,typename CompatiblePred
>
iterator find(
const key_type& k,
const CompatibleHash& hash,const CompatiblePred& eq,mpl::true_)const
{
return find(k,hash,eq,mpl::false());
}
template<
typename CompatibleKey,typename CompatibleHash,typename CompatiblePred
>
iterator find(
const CompatibleKey& k,
const CompatibleHash& hash,const CompatiblePred& eq,mpl::false_)const
{
std::size_t buc=buckets.position(hash(k));
for(node_impl_pointer x=buckets.at(buc)->prior();
x!=node_impl_pointer(0);x=node_alg::next_to_inspect(x)){
if(eq(k,key(node_type::from_impl(x)->value()))){
return make_iterator(node_type::from_impl(x));
}
}
return end();
}
template<
typename CompatibleHash,typename CompatiblePred
>
size_type count(
const key_type& k,
const CompatibleHash& hash,const CompatiblePred& eq,mpl::true_)const
{
return count(k,hash,eq,mpl::false());
}
template<
typename CompatibleKey,typename CompatibleHash,typename CompatiblePred
>
size_type count(
const CompatibleKey& k,
const CompatibleHash& hash,const CompatiblePred& eq,mpl::false_)const
{
std::size_t buc=buckets.position(hash(k));
for(node_impl_pointer x=buckets.at(buc)->prior();
x!=node_impl_pointer(0);x=node_alg::next_to_inspect(x)){
if(eq(k,key(node_type::from_impl(x)->value()))){
size_type res=0;
node_impl_pointer y=end_of_range(x);
do{
++res;
x=node_alg::after(x);
}while(x!=y);
return res;
}
}
return 0;
}
template<
typename CompatibleHash,typename CompatiblePred
>
std::pair<iterator,iterator> equal_range(
const key_type& k,
const CompatibleHash& hash,const CompatiblePred& eq,mpl::true_)const
{
return equal_range(k,hash,eq,mpl::false_());
}
template<
typename CompatibleKey,typename CompatibleHash,typename CompatiblePred
>
std::pair<iterator,iterator> equal_range(
const CompatibleKey& k,
const CompatibleHash& hash,const CompatiblePred& eq,mpl::false_)const
{
std::size_t buc=buckets.position(hash(k));
for(node_impl_pointer x=buckets.at(buc)->prior();
x!=node_impl_pointer(0);x=node_alg::next_to_inspect(x)){
if(eq(k,key(node_type::from_impl(x)->value()))){
return std::pair<iterator,iterator>(
make_iterator(node_type::from_impl(x)),
make_iterator(node_type::from_impl(end_of_range(x))));
}
}
return std::pair<iterator,iterator>(end(),end());
}
key_from_value key;
hasher hash_;
key_equal eq_;

View File

@ -1,6 +1,6 @@
/* Boost.MultiIndex test for standard set operations.
*
* Copyright 2003-2013 Joaquin M Lopez Munoz.
* Copyright 2003-2014 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)
@ -19,6 +19,34 @@
using namespace boost::multi_index;
struct type1{};
struct type2
{
private:
operator type1()const{return type1();}
};
struct less_type12
{
bool operator()(type1,type1)const{return false;}
bool operator()(type1,type2)const{return false;}
bool operator()(type2,type1)const{return false;}
};
struct hash_type12
{
std::size_t operator()(type1)const{return 0;}
std::size_t operator()(type2)const{return 0;}
};
struct eq_type12
{
bool operator()(type1,type1)const{return true;}
bool operator()(type1,type2)const{return true;}
bool operator()(type2,type1)const{return true;}
};
void test_set_ops()
{
employee_set es;
@ -55,4 +83,26 @@ void test_set_ops()
std::pair<employee_set_by_age::iterator,employee_set_by_age::iterator> p2=
i2.equal_range(30);
BOOST_TEST(p2.first==p2.second&&p2.first->age==31);
/* check promotion detection plays nice with private conversion */
multi_index_container<
type1,
indexed_by<
ordered_unique<identity<type1>,less_type12>,
hashed_unique<identity<type1>,hash_type12,eq_type12>
>
> c;
c.insert(type1());
BOOST_TEST(c.find(type2())==c.begin());
BOOST_TEST(c.count(type2())==1);
BOOST_TEST(c.lower_bound(type2())==c.begin());
BOOST_TEST(c.upper_bound(type2())==c.end());
BOOST_TEST(c.equal_range(type2())==std::make_pair(c.begin(),c.end()));
BOOST_TEST(c.get<1>().find(type2())==c.get<1>().begin());
BOOST_TEST(c.get<1>().count(type2())==1);
BOOST_TEST(c.get<1>().equal_range(type2())==
std::make_pair(c.get<1>().begin(),c.get<1>().end()));
}