У меня есть такой класс (упрощенный пример):
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
должен быть непрозрачным объектом, и я не должен использовать его внутренности.
Поэтому мой вопрос заключается в том, как создать необходимый индекс и правильный компаратор?
Что касается вашего исходного решения, вам не нужно использовать составные ключи для вашего второго индекса в качестве 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::string
s: в этой ситуации существуют неявные преобразования, и, казалось бы, кортежи размером 1, 2 и 3 являются одинаково хорошими кандидатами (проблема реализации с кортежами ИМХО.) Решение состоит в том, чтобы шаблонизировать эти LBFCompare::operator()
с, которые принимают кортежи.
Очень интересная проблема! Самый простой способ решить эту проблему — добавить следующее в ваш класс
class A {
...
float getTaggedValue()const{return getFlag()?-getValue():getValue();}
...
};
а затем снабдите свой второй индекс обычным составным ключом (label,tag,tagged_value)
При выполнении поиска с кортежами (l,t,v)
ты не должен забывать иметь v
Отрицается, если тег верен (немного больше усилий, вы можете иметь getTaggedValue
вернуть специальный тип, скажем, pair<bool, double>
, чтобы вы случайно не пропустили непроверенный плавающий элемент в кортеж.)