Увеличить контейнер multi_index с помощью составного ключа, пользовательского компаратора и поиска по частичному индексу

У меня есть такой класс (упрощенный пример):

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:
...
};

Мне нужен контейнер, который индексируется уникальной комбинацией (id, label) а также уникальное сочетание (label, flag, value), Мне также нужно, чтобы он сортировался по второму индексу по метке сначала, за которой следует флаг, за которым следуют уменьшающиеся значения, если флаг равен true, и увеличивающиеся значения, если флаг равен false. Итак, после создания ключевых экстракторов я делаю что-то вроде этого:

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;

где сравнение определяется как:

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);
}
};

Теперь я могу выполнять такие запросы:

Items_by_label_flag_value::const_iterator it = items_by_label_flag_value.find(boost::make_tuple("A", true, 0.1));

Однако, если я попытаюсь выполнить частичный запрос, скажем, получить все элементы с одинаковой меткой, мой код не скомпилируется:

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"));

Я знаю, почему он не компилируется: в компараторе я явно использую .get<0>(), .get<1>() а также .get<2>() но частичный поиск кортежа не имеет <1> а также <2> элементы. Чего я не знаю, так это как создать правильный компаратор. Если я попытаюсь добавить к нему еще две функции, которые принимают кортежи только одного элемента, то компилятор жалуется на неоднозначность в operator() вызов.

Я тоже понимаю что composite_key_result должен быть непрозрачным объектом, и я не должен использовать его внутренности.

Поэтому мой вопрос заключается в том, как создать необходимый индекс и правильный компаратор?

3

Решение

Что касается вашего исходного решения, вам не нужно использовать составные ключи для вашего второго индекса в качестве Compare экстрактор в основном пытается заменить оборудование, автоматически генерируемое composite_key/composite_key_compare, Следующее было (слегка) проверено, чтобы работать:

struct LBFCompare
{
bool operator() (const A& x, const A& y) const {
return compare(
x.getLabel(), x.getFlag(), x.getValue(),
y.getLabel(), y.getFlag(), y.getValue());
}
bool operator() (const std::string& x, const A& y) const{
return compare(x,y.getLabel());
}
bool operator() (const A& x, const std::string& y) const{
return compare(x.getLabel(),y);
}
template<typename T0>
bool operator() (const boost::tuple<T0>& x, const A& y) const{
return compare(x.get<0>(),y.getLabel());
}
template<typename T0>
bool operator() (const A& x, const boost::tuple<T0>& y) const{
return compare(x.getLabel(),y.get<0>());
}
template<typename T0,typename T1>
bool operator() (const boost::tuple<T0,T1>& x, const A& y) const{
return compare(x.get<0>(),x.get<1>(),y.getLabel(),y.getFlag());
}
template<typename T0,typename T1>
bool operator() (const A& x, const boost::tuple<T0,T1>& y) const{
return compare(x.getLabel(),x.getFlag(),y.get<0>(),y.get<1>());
}
template<typename T0,typename T1,typename T2>
bool operator() (const boost::tuple<T0,T1,T2>& x, const A& y) const{
return compare(x.get<0>(),x.get<1>(),x.get<2>(),y.getLabel(),y.getFlag(),y.getValue());
}
template<typename T0,typename T1,typename T2>
bool operator() (const A& x, const boost::tuple<T0,T1,T2>& y) const{
return compare(x.getLabel(),x.getFlag(),x.getValue(),y.get<0>(),y.get<1>(),y.get<2>());
}
bool compare(const std::string& l1, const std::string& l2) const {
return l1 < l2;
}
bool compare(const std::string& l1, bool f1, const std::string& l2, bool f2) const {
if (l1 != l2) return l1 < l2;
return f1 < f2;
}
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);
}
};

struct by_id_label{};
struct by_label_flag_value{};

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>,
boost::multi_index::identity<A>,
LBFCompare
>
>
> Items;
typedef Items::index<by_label_flag_value>::type Items_by_label_flag_value;

int main()
{
Items c;
c.insert(Ptr(new A("id","label",true,1.0)));
Items_by_label_flag_value& i=c.get<by_label_flag_value>();
i.find("id");
i.find(boost::make_tuple("id"));
i.find(boost::make_tuple("id",true));
i.find(boost::make_tuple("id",true,1.0));
}

Проблемы с неоднозначностью, о которых вы упоминаете, связаны с тем, что вы, вероятно, смотрите вверх, передавая кортежи с const char*скорее, чем полностью сформированный std::strings: в этой ситуации существуют неявные преобразования, и, казалось бы, кортежи размером 1, 2 и 3 являются одинаково хорошими кандидатами (проблема реализации с кортежами ИМХО.) Решение состоит в том, чтобы шаблонизировать эти LBFCompare::operator()с, которые принимают кортежи.

4

Другие решения

Очень интересная проблема! Самый простой способ решить эту проблему — добавить следующее в ваш класс

class A {
...
float getTaggedValue()const{return getFlag()?-getValue():getValue();}
...
};

а затем снабдите свой второй индекс обычным составным ключом (label,tag,tagged_value) При выполнении поиска с кортежами (l,t,v) ты не должен забывать иметь vОтрицается, если тег верен (немного больше усилий, вы можете иметь getTaggedValue вернуть специальный тип, скажем, pair<bool, double>, чтобы вы случайно не пропустили непроверенный плавающий элемент в кортеж.)

2