I have a class like this (simplified example):
class A {
public:
typedef boost::shared_ptr<A> Ptr;
const std::string& getID() const;
const std::string& getLabel() const;
bool getFlag() const;
float getValue() const;
private:
...
};
I need a container that is indexed by a unique combination of (id, label)
and also a unique combination of (label, flag, value)
. I also need it to be sorted by the second index by label first followed by flag followed by decreasing values if flag is true and increasing values if flag is false. So after creating key extractors I'm doing something like this:
typedef boost::multi_index::composite_key<
Ptr, extractor_id, extractor_label
> id_label_key;
typedef boost::multi_index::composite_key<
Ptr, extractor_label, extractor_flag, extractor_value
> label_flag_value_key;
...
typedef boost::multi_index_container<Ptr,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<
boost::multi_index::tag<by_id_label
id_label_key
>,
boost::multi_index::ordered_unique<
boost::multi_index::tag<by_label_flag_value>,
label_flag_value_key,
Compare
>,
>
> Items;
typedef Items::index<by_label_flag_value>::type Items_by_label_flag_value;
where Compare is defined as:
struct Compare {
bool operator() (const boost::multi_index::composite_key_result<label_flag_value_key>& k, const boost::tuple<float,bool>& q) const {
return compare(k.value->getLabel(), k.value->getFlag(), k.value->getValue(),
q.get<0>(), q.get<1>(), q.get<2>()
}
bool operator() (const boost::tuple<float,bool>& q, const boost::multi_index::composite_key_result<label_flag_value_key>& k) const {
return compare(q.get<0>(), q.get<1>(), q.get<2>(),
k.value->getLabel(), k.value->getFlag(), k.value->getValue(),
}
bool operator() (const boost::multi_index::composite_key_result<label_flag_value_key>& k1, const boost::multi_index::composite_key_result<label_flag_value_key>& k2) const {
return compare(k1.value->getLabel(), k1.value->getFlag(), k1.value->getValue(),
k2.value->getLabel(), k2.value->getFlag(), k2.value->getValue())
}
bool compare(const std::string& l1, bool f1, float v1, const std::string& l2, bool f2, float v2) const {
if (l1 != l2) return l1 < l2;
if (f1 != f2) return f1;
return f1 ? (v1 > v2) : (v1 < v2);
}
};
Now, I can perform queries like this:
Items_by_label_flag_value::const_iterator it = items_by_label_flag_value.find(boost::make_tuple("A", true, 0.1));
However, if I try to perform a partial query - say, retrieve all items that have the same label - my code won't compile:
std::pair<Items_by_label_flag_value::const_iterator, Items_by_label_flag_value::const_iterator> range = items_by_label_flag_value.equal_range(boost::make_tuple("A"));
I know why it does not compile: in the comparer I explicitly use .get<0>()
, .get<1>()
and .get<2>()
but partial search tuple does not have <1>
and <2>
elements. What I do not know is how to create the correct comparer. If I try to add two more functions to it that takes tuples of one element only then the compiler complains of ambiguity in operator()
call.
I also understand that composite_key_result
is supposed to be an opaque object and I should not be using its insides.
So my question is how to create the required index and the correct comparer?