/* Copyright 2003-2025 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_COMPOSITE_KEY_HPP #define BOOST_MULTI_INDEX_COMPOSITE_KEY_HPP #if defined(_MSC_VER) #pragma once #endif #include /* keep it first to prevent nasty warns in MSVC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* A composite key stores n key extractors and "computes" the * result on a given value as a packed reference to the value and * the composite key itself. Actual invocations to the component * key extractors are lazily performed when executing an operation * on composite_key results (equality, comparison, hashing.) * As the other key extractors in Boost.MultiIndex, composite_key * is overloaded to work on chained pointers to T and reference_wrappers * of T. */ namespace boost{ template class reference_wrapper; /* fwd decl. */ namespace multi_index{ namespace detail{ /* Given R = composite_key_result>, * instantiate_with_transformed_keys is the type * Class< F... >. */ template class F> struct instantiate_with_transformed_keys_helper { template using fn=F; }; template< template class Class,template class F, typename CompositeKeyResult > using instantiate_with_transformed_keys=mp11::mp_rename< mp11::mp_transform_q< instantiate_with_transformed_keys_helper, typename CompositeKeyResult::composite_key_type::key_extractor_tuple >, Class >; /* used for defining equality and comparison ops of composite_key_result */ struct cons_generic_operator_equal { cons_generic_operator_equal get_head()const{return {};} cons_generic_operator_equal get_tail()const{return {};} template bool operator()(const T& x,const Q& y)const{return x==y;} }; struct cons_generic_operator_less { cons_generic_operator_less get_head()const{return {};} cons_generic_operator_less get_tail()const{return {};} template bool operator()(const T& x,const Q& y)const{return x bool equal_ckey_ckey( const tuples::null_type&,const Value1&, const tuples::null_type&,const Value2&, const EqualCons&) { return true; } template< typename KeyCons1,typename Value1,typename KeyCons2, typename Value2, typename EqualCons > bool equal_ckey_ckey( const KeyCons1& c0,const Value1& v0,const KeyCons2& c1,const Value2& v1, const EqualCons& eq) { if(!eq.get_head()(c0.get_head()(v0),c1.get_head()(v1)))return false; return equal_ckey_ckey(c0.get_tail(),v0,c1.get_tail(),v1,eq.get_tail()); } template bool equal_ckey_cval( const tuples::null_type&,const Value&,const tuples::null_type&, const EqualCons&) { return true; } template bool equal_ckey_cval( const KeyCons& c,const Value& v,const ValCons& vc,const EqualCons& eq) { if(!eq.get_head()(c.get_head()(v),vc.get_head()))return false; return equal_ckey_cval(c.get_tail(),v,vc.get_tail(),eq.get_tail()); } template bool equal_cval_ckey( const tuples::null_type&,const tuples::null_type&,const Value&, const EqualCons&) { return true; } template bool equal_cval_ckey( const ValCons& vc,const KeyCons& c,const Value& v,const EqualCons& eq) { if(!eq.get_head()(vc.get_head(),c.get_head()(v)))return false; return equal_cval_ckey(vc.get_tail(),c.get_tail(),v,eq.get_tail()); } template< typename Value1,typename KeyCons2,typename Value2,typename CompareCons > bool compare_ckey_ckey( const tuples::null_type&,const Value1&, const KeyCons2&,const Value2&, const CompareCons&) { return false; } template< typename KeyCons1,typename Value1,typename Value2,typename CompareCons > bool compare_ckey_ckey( const KeyCons1&,const Value1&,const tuples::null_type&,const Value2&, const CompareCons&) { return false; } template bool compare_ckey_ckey( const tuples::null_type&,const Value1&, const tuples::null_type&,const Value2&, const CompareCons&) { return false; } template< typename KeyCons1,typename Value1,typename KeyCons2, typename Value2, typename CompareCons > bool compare_ckey_ckey( const KeyCons1& c0,const Value1& v0, const KeyCons2& c1,const Value2& v1, const CompareCons& comp) { if(comp.get_head()(c0.get_head()(v0),c1.get_head()(v1)))return true; if(comp.get_head()(c1.get_head()(v1),c0.get_head()(v0)))return false; return compare_ckey_ckey(c0.get_tail(),v0,c1.get_tail(),v1,comp.get_tail()); } template bool compare_ckey_cval( const tuples::null_type&,const Value&,const ValCons&,const CompareCons&) { return false; } template bool compare_ckey_cval( const KeyCons&,const Value&,const tuples::null_type&,const CompareCons&) { return false; } template bool compare_ckey_cval( const tuples::null_type&,const Value&,const tuples::null_type&, const CompareCons&) { return false; } template bool compare_ckey_cval( const KeyCons& c,const Value& v,const ValCons& vc, const CompareCons& comp) { if(comp.get_head()(c.get_head()(v),vc.get_head()))return true; if(comp.get_head()(vc.get_head(),c.get_head()(v)))return false; return compare_ckey_cval(c.get_tail(),v,vc.get_tail(),comp.get_tail()); } template bool compare_cval_ckey( const ValCons&,const tuples::null_type&,const Value&,const CompareCons&) { return false; } template bool compare_cval_ckey( const tuples::null_type&,const KeyCons&,const Value&,const CompareCons&) { return false; } template bool compare_cval_ckey( const tuples::null_type&,const tuples::null_type&,const Value&, const CompareCons&) { return false; } template bool compare_cval_ckey( const ValCons& vc,const KeyCons& c,const Value& v, const CompareCons& comp) { if(comp.get_head()(vc.get_head(),c.get_head()(v)))return true; if(comp.get_head()(c.get_head()(v),vc.get_head()))return false; return compare_cval_ckey(vc.get_tail(),c.get_tail(),v,comp.get_tail()); } template std::size_t hash_ckey( const tuples::null_type&,const Value&,const tuples::null_type&, std::size_t carry) { return carry; } template std::size_t hash_ckey( const KeyCons& c,const Value& v,const HashCons& h,std::size_t carry=0) { /* same hashing formula as boost::hash_combine */ carry^=h.get_head()(c.get_head()(v))+0x9e3779b9+(carry<<6)+(carry>>2); return hash_ckey(c.get_tail(),v,h.get_tail(),carry); } inline std::size_t hash_cval( const tuples::null_type&,const tuples::null_type&,std::size_t carry) { return carry; } template std::size_t hash_cval(const ValCons& vc,const HashCons& h,std::size_t carry=0) { carry^=h.get_head()(vc.get_head())+0x9e3779b9+(carry<<6)+(carry>>2); return hash_cval(vc.get_tail(),h.get_tail(),carry); } } /* namespace multi_index::detail */ /* composite_key_result */ #if defined(BOOST_MSVC) #pragma warning(push) #pragma warning(disable:4512) #endif template struct composite_key_result { typedef CompositeKey composite_key_type; typedef typename composite_key_type::value_type value_type; composite_key_result( const composite_key_type& composite_key_,const value_type& value_): composite_key(composite_key_),value(value_) {} const composite_key_type& composite_key; const value_type& value; }; #if defined(BOOST_MSVC) #pragma warning(pop) #endif /* composite_key */ template struct composite_key: private detail::augmented_stdtuple { private: typedef detail::augmented_stdtuple super; public: typedef std::tuple key_extractor_tuple; typedef Value value_type; typedef composite_key_result result_type; using super::super; composite_key()=default; composite_key(const composite_key&)=default; const key_extractor_tuple& key_extractors()const{return *this;} key_extractor_tuple& key_extractors(){return *this;} template typename disable_if< is_convertible,result_type>::type operator()(const ChainedPtr& x)const { return operator()(*x); } result_type operator()(const value_type& x)const { return result_type(*this,x); } result_type operator()(const reference_wrapper& x)const { return result_type(*this,x.get()); } result_type operator()(const reference_wrapper& x)const { return result_type(*this,x.get()); } }; /* comparison operators */ /* == */ template inline bool operator==( const composite_key_result& x, const composite_key_result& y) { typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::equal_ckey_ckey( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y.composite_key.key_extractors()),y.value, detail::cons_generic_operator_equal()); } template inline bool operator==( const composite_key_result& x, const boost::tuple& y) { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef boost::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::equal_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, y,detail::cons_generic_operator_equal()); } template inline bool operator==( const boost::tuple& x, const composite_key_result& y) { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::equal_cval_ckey( x,detail::make_cons_stdtuple(y.composite_key.key_extractors()), y.value,detail::cons_generic_operator_equal()); } template inline bool operator==( const composite_key_result& x, const std::tuple& y) { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef std::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::equal_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y),detail::cons_generic_operator_equal()); } template inline bool operator==( const std::tuple& x, const composite_key_result& y) { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef std::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::equal_cval_ckey( detail::make_cons_stdtuple(x), detail::make_cons_stdtuple(y.composite_key.key_extractors()),y.value, detail::cons_generic_operator_equal()); } /* < */ template inline bool operator<( const composite_key_result& x, const composite_key_result& y) { return detail::compare_ckey_ckey( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y.composite_key.key_extractors()),y.value, detail::cons_generic_operator_less()); } template inline bool operator<( const composite_key_result& x, const boost::tuple& y) { return detail::compare_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, y,detail::cons_generic_operator_less()); } template inline bool operator<( const boost::tuple& x, const composite_key_result& y) { return detail::compare_cval_ckey( x,detail::make_cons_stdtuple(y.composite_key.key_extractors()), y.value,detail::cons_generic_operator_less()); } template inline bool operator<( const composite_key_result& x, const std::tuple& y) { return detail::compare_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y),detail::cons_generic_operator_less()); } template inline bool operator<( const std::tuple& x, const composite_key_result& y) { return detail::compare_cval_ckey( detail::make_cons_stdtuple(x), detail::make_cons_stdtuple(y.composite_key.key_extractors()),y.value, detail::cons_generic_operator_less()); } /* rest of comparison operators */ #define BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS(t1,t2,a1,a2) \ template inline bool operator!=(const a1& x,const a2& y) \ { \ return !(x==y); \ } \ \ template inline bool operator>(const a1& x,const a2& y) \ { \ return y inline bool operator>=(const a1& x,const a2& y) \ { \ return !(x inline bool operator<=(const a1& x,const a2& y) \ { \ return !(y, composite_key_result ) BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( typename CompositeKey, typename... Values, composite_key_result, boost::tuple ) BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( typename... Values, typename CompositeKey, boost::tuple, composite_key_result ) BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( typename CompositeKey, typename... Values, composite_key_result, std::tuple ) BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( typename... Values, typename CompositeKey, std::tuple, composite_key_result ) #undef BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS /* composite_key_equal_to */ template struct composite_key_equal_to: private detail::augmented_stdtuple { private: typedef detail::augmented_stdtuple super; public: typedef std::tuple key_eq_tuple; using super::super; composite_key_equal_to()=default; composite_key_equal_to(const composite_key_equal_to&)=default; const key_eq_tuple& key_eqs()const{return *this;} key_eq_tuple& key_eqs(){return *this;} template bool operator()( const composite_key_result & x, const composite_key_result & y)const { typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value&& std::tuple_size::value== std::tuple_size::value); return detail::equal_ckey_ckey( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y.composite_key.key_extractors()),y.value, detail::make_cons_stdtuple(key_eqs())); } template bool operator()( const composite_key_result& x, const boost::tuple& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef boost::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value&& std::tuple_size::value== std::tuple_size::value); return detail::equal_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, y,detail::make_cons_stdtuple(key_eqs())); } template bool operator()( const boost::tuple& x, const composite_key_result& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef boost::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value&& std::tuple_size::value== std::tuple_size::value); return detail::equal_cval_ckey( x,detail::make_cons_stdtuple(y.composite_key.key_extractors()), y.value,detail::make_cons_stdtuple(key_eqs())); } template bool operator()( const composite_key_result& x, const std::tuple& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef std::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value&& std::tuple_size::value== std::tuple_size::value); return detail::equal_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y), detail::make_cons_stdtuple(key_eqs())); } template bool operator()( const std::tuple& x, const composite_key_result& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef std::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value&& std::tuple_size::value== std::tuple_size::value); return detail::equal_cval_ckey( detail::make_cons_stdtuple(x), detail::make_cons_stdtuple(y.composite_key.key_extractors()), y.value,detail::make_cons_stdtuple(key_eqs())); } }; /* composite_key_compare */ template struct composite_key_compare: private detail::augmented_stdtuple { private: typedef detail::augmented_stdtuple super; public: typedef std::tuple key_comp_tuple; using super::super; composite_key_compare()=default; composite_key_compare(const composite_key_compare&)=default; const key_comp_tuple& key_comps()const{return *this;} key_comp_tuple& key_comps(){return *this;} template bool operator()( const composite_key_result & x, const composite_key_result & y)const { typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value|| std::tuple_size::value<= std::tuple_size::value); return detail::compare_ckey_ckey( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y.composite_key.key_extractors()),y.value, detail::make_cons_stdtuple(key_comps())); } template bool operator()( const composite_key_result& x, const Value& y)const { return operator()(x,boost::make_tuple(boost::cref(y))); } template bool operator()( const composite_key_result& x, const boost::tuple& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef boost::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value|| std::tuple_size::value<= std::tuple_size::value); return detail::compare_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, y,detail::make_cons_stdtuple(key_comps())); } template bool operator()( const Value& x, const composite_key_result& y)const { return operator()(boost::make_tuple(boost::cref(x)),y); } template bool operator()( const boost::tuple& x, const composite_key_result& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef boost::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value|| std::tuple_size::value<= std::tuple_size::value); return detail::compare_cval_ckey( x,detail::make_cons_stdtuple(y.composite_key.key_extractors()), y.value,detail::make_cons_stdtuple(key_comps())); } template bool operator()( const composite_key_result& x, const std::tuple& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef std::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value|| std::tuple_size::value<= std::tuple_size::value); return detail::compare_ckey_cval( detail::make_cons_stdtuple(x.composite_key.key_extractors()),x.value, detail::make_cons_stdtuple(y), detail::make_cons_stdtuple(key_comps())); } template bool operator()( const std::tuple& x, const composite_key_result& y)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; typedef std::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value<= std::tuple_size::value|| std::tuple_size::value<= std::tuple_size::value); return detail::compare_cval_ckey( detail::make_cons_stdtuple(x), detail::make_cons_stdtuple(y.composite_key.key_extractors()), y.value,detail::make_cons_stdtuple(key_comps())); } }; /* composite_key_hash */ template struct composite_key_hash:private detail::augmented_stdtuple { private: typedef detail::augmented_stdtuple super; public: typedef std::tuple key_hasher_tuple; using super::super; composite_key_hash()=default; composite_key_hash(const composite_key_hash&)=default; const key_hasher_tuple& key_hash_functions()const{return *this;} key_hasher_tuple& key_hash_functions(){return *this;} template std::size_t operator()(const composite_key_result & x)const { typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::hash_ckey( detail::make_cons_stdtuple(x.composite_key.key_extractors()), x.value,detail::make_cons_stdtuple(key_hash_functions())); } template std::size_t operator()(const boost::tuple& x)const { typedef boost::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::hash_cval( x,detail::make_cons_stdtuple(key_hash_functions())); } template std::size_t operator()(const std::tuple& x)const { typedef std::tuple key_tuple; BOOST_STATIC_ASSERT( std::tuple_size::value== std::tuple_size::value); return detail::hash_cval( detail::make_cons_stdtuple(x), detail::make_cons_stdtuple(key_hash_functions())); } }; /* Instantiations of the former functors with "natural" basic components: * composite_key_result_equal_to uses std::equal_to of the values. * composite_key_result_less uses std::less. * composite_key_result_greater uses std::greater. * composite_key_result_hash uses boost::hash. */ template struct composite_key_result_equal_to: private detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_equal_to,std::equal_to,CompositeKeyResult > { private: typedef detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_equal_to,std::equal_to,CompositeKeyResult > super; public: typedef CompositeKeyResult first_argument_type; typedef first_argument_type second_argument_type; typedef bool result_type; using super::operator(); }; template struct composite_key_result_less: private detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_compare,std::less,CompositeKeyResult > { private: typedef detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_compare,std::less,CompositeKeyResult > super; public: typedef CompositeKeyResult first_argument_type; typedef first_argument_type second_argument_type; typedef bool result_type; using super::operator(); }; template struct composite_key_result_greater: private detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_compare,std::greater,CompositeKeyResult > { private: typedef detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_compare,std::greater,CompositeKeyResult > super; public: typedef CompositeKeyResult first_argument_type; typedef first_argument_type second_argument_type; typedef bool result_type; using super::operator(); }; template struct composite_key_result_hash: private detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_hash,boost::hash,CompositeKeyResult > { private: typedef detail::instantiate_with_transformed_keys< boost::multi_index::composite_key_hash,boost::hash,CompositeKeyResult > super; public: typedef CompositeKeyResult argument_type; typedef std::size_t result_type; using super::operator(); }; } /* namespace multi_index */ } /* namespace boost */ /* Specializations of std::equal_to, std::less, std::greater and boost::hash * for composite_key_results enabling interoperation with tuples of values. */ namespace std{ template struct equal_to >: boost::multi_index::composite_key_result_equal_to< boost::multi_index::composite_key_result > { }; template struct less >: boost::multi_index::composite_key_result_less< boost::multi_index::composite_key_result > { }; template struct greater >: boost::multi_index::composite_key_result_greater< boost::multi_index::composite_key_result > { }; } /* namespace std */ namespace boost{ template struct hash >: boost::multi_index::composite_key_result_hash< boost::multi_index::composite_key_result > { }; } /* namespace boost */ #endif