|
Boost Users : |
Subject: Re: [Boost-users] [MultiIndex] is it possible to create multi-key key-extractors?
From: joaquin_at_[hidden]
Date: 2010-08-09 07:51:49
Steven Watanabe escribió:
> AMDG
>
> Mostafa wrote:
>
>> That's pretty much what I figured from the documentation, I was just
>> hoping for some kind of workaround that I might have missed.
>>
>> Well, here goes a feature request ... (A rough outline.)
>>
>> I propose adding a key-extractorS concept to the multi-index library.
>> Something along the lines of the existing key-extractor concept,
>> modified such that
>>
>> key_extractorS::result_type is a Boost.Range of keys
>>
>
> Sorry, but I don't think that this can be implemented
> in a way that is consistent with the rest of the library.
> You'd be better off using an auxillary map of pointers.
> No implementation that I can think of is going to be
> more efficient than that, anyway.
>
Adding to Steven's answer, you'll need to somehow maintain additional data
structures to do what you want. As each element has one and only one key
per (key-based) index, you need an extra level of indirection that maps
all the aliases into a single value that can act as the key. A possible
way to
do this is by using a "multi-key bag" (for want of a better name) that you
can feed with lists of keys to obtain a representative token of the list in
return (a N-to-1 bimap, really). Something like this:
template<typename Key>
class multi_key_bag
{
public:
typedef Key key_type;
typedef std::size_t token_type;
static const token_type not_found=static_cast<token_type>(-1);
multi_key_bag():next_token(0){}
template<typename ForwardIterator>
token_type insert(ForwardIterator first,ForwardIterator last)
{
if(first==last)return not_found;
token_type token=find(*first);
if(token!=not_found)return token;
token=++next_token;
while(first!=last)cont.insert(value_type(*first++,token));
return token;
};
token_type find(const key_type& k)const
{
container::iterator it=cont.find(k);
if(it!=cont.end())return it->token;
else return not_found;
}
void erase(const key_type& k)
{
token_type token=find(k);
if(token==not_found)return;
cont.template get<1>().erase(token);
}
private:
struct value_type
{
value_type(const key_type& k,token_type t):key(k),token(t){}
key_type key;
token_type token;
};
typedef multi_index_container<
value_type,
indexed_by<
hashed_unique<member<value_type,key_type,&value_type::key> >,
hashed_non_unique<member<value_type,token_type,&value_type::token> >
>
> container;
container cont;
token_type next_token;
};
The semantics of multi_key_bag is simple: you insert N keys and obtain a
token in return to be used as the representative key; each of the N keys
can be used in isolation to retrieve that same token; erasing a key
automatically
erases the other associated key as well.
Now, a suitable key extractor would have to be provided with a reference
to a multi-key bag so as to do its job:
struct FlowerMultiKeyExtractor
{
typedef multi_key_bag<std::string> MultiKeyBag;
typedef MultiKeyBag::token_type result_type;
FlowerMultiKeyExtractor(MultiKeyBag& bag):pbag(&bag){}
result_type operator()(const Flower& f)const
{
if(f.aliases.empty())return MultiKeyBag::not_found;
return pbag->find(*(f.aliases.begin()));
}
private:
MultiKeyBag* pbag;
};
And that's it: the ugly part is that you need to manually maintain a
multi-key bag in sync with your main container. I've attached a complete
demo
program. Beware: the mechanism by which the tokens are generated is rather
frail (an incrementing counter) since it'll provide duplicate tokens if
you do a lot
of insertions (i.e., when the counter wraps around). A more robust
implementation is left as an exercise to you.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
using namespace boost::multi_index;
template<typename Key>
class multi_key_bag
{
public:
typedef Key key_type;
typedef std::size_t token_type;
static const token_type not_found=static_cast<token_type>(-1);
multi_key_bag():next_token(0){}
template<typename ForwardIterator>
token_type insert(ForwardIterator first,ForwardIterator last)
{
if(first==last)return not_found;
token_type token=find(*first);
if(token!=not_found)return token;
token=++next_token;
while(first!=last)cont.insert(value_type(*first++,token));
return token;
};
token_type find(const key_type& k)const
{
container::iterator it=cont.find(k);
if(it!=cont.end())return it->token;
else return not_found;
}
void erase(const key_type& k)
{
token_type token=find(k);
if(token==not_found)return;
cont.template get<1>().erase(token);
}
private:
struct value_type
{
value_type(const key_type& k,token_type t):key(k),token(t){}
key_type key;
token_type token;
};
typedef multi_index_container<
value_type,
indexed_by<
hashed_unique<member<value_type,key_type,&value_type::key> >,
hashed_non_unique<member<value_type,token_type,&value_type::token> >
>
> container;
container cont;
token_type next_token;
};
#include <boost/assign/list_of.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <list>
#include <string>
#include <cassert>
using namespace boost::assign;
struct Flower
{
std::list<std::string> aliases;
};
struct FlowerMultiKeyExtractor
{
typedef multi_key_bag<std::string> MultiKeyBag;
typedef MultiKeyBag::token_type result_type;
FlowerMultiKeyExtractor(MultiKeyBag& bag):pbag(&bag){}
result_type operator()(const Flower& f)const
{
if(f.aliases.empty())return MultiKeyBag::not_found;
return pbag->find(*(f.aliases.begin()));
}
private:
MultiKeyBag* pbag;
};
typedef boost::multi_index_container<
Flower,
indexed_by<
ordered_unique<FlowerMultiKeyExtractor>
>
> FlowerMngr;
int main()
{
FlowerMultiKeyExtractor::MultiKeyBag bag;
FlowerMultiKeyExtractor ext((bag));
FlowerMngr mgr(
FlowerMngr::ctor_args_list(boost::make_tuple(ext)));
Flower f0;
f0.aliases=list_of("rose")("alba")("gallica");
bag.insert(f0.aliases.begin(),f0.aliases.end());
mgr.insert(f0);
Flower f1;
f1.aliases=list_of("narcissus")("daffodil")("jonquil");
bag.insert(f1.aliases.begin(),f1.aliases.end());
mgr.insert(f1);
assert(*(mgr.find(bag.find("rose"))->aliases.begin())=="rose");
assert(*(mgr.find(bag.find("alba"))->aliases.begin())=="rose");
assert(*(mgr.find(bag.find("jonquil"))->aliases.begin())=="narcissus");
assert(bag.find("daisy")==FlowerMultiKeyExtractor::MultiKeyBag::not_found);
mgr.erase(bag.find("jonquil"));
bag.erase("jonquil");
assert(bag.find("narcissus")==FlowerMultiKeyExtractor::MultiKeyBag::not_found);
}
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net