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(-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 >,
hashed_non_unique >
>
> 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_bagstd::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
#include
#include
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(-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 >,
hashed_non_unique >
>
container;
container cont;
token_type next_token;
};
#include
#include
#include <list>
#include <string>
#include <cassert>
using namespace boost::assign;
struct Flower
{
std::liststd::string aliases;
};
struct FlowerMultiKeyExtractor
{
typedef multi_key_bagstd::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);
}