2013-05-07 94 views
3

我有这样的(简化的示例)的类:升压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)的独特组合。我还需要按照标签第一个索引对标签进行排序,如果标志为真,则标志后跟值减小,如果标志为假,则增加值。因此,在创建键提取后,我在做这样的事情:

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应该是一个不透明的对象,我不应该使用它的内部。

所以我的问题是如何创建所需的索引和正确的比较?

回答

4

由于对于您的原始解决方案,您不需要为第二个索引使用组合键作为您的Compare ex拖拉机基本上试图取代由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正在找了含糊不清的问题:在这种情况存在隐式转换,看起来1,2和3大小的元组是同样好的候选元素(对于元组恕我直言,这是一个实现问题)。解决方案是模拟那些带有元组的元素。

2

非常有趣的问题!要解决它,我能想到的最简单的方法是:将以下添加到您的类

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

,然后用常规的复合键装备你的第二个指数(label,tag,tagged_value)当元组做搜索(l,t,v)你一定不要忘记有v否定如果标记为真(多一点的努力,你可以有getTaggedValue返回一个特殊类型,说pair<bool, double>,让你不小心直接一个未经检查的浮动传递到元组。)

+0

谢谢!是的,我也是这样做的(这也使得自定义比较器不必要),我认为这是一个正确的解决方案。另一个类似的方法是有两个变量来存储值,一个用值填充,另一个用零填充,这取决于标志;当值类型不是float时,这个方法会更好,但是对于复制而言相对昂贵的东西(例如某种Decimal),这样我们总是可以在key提取器中返回const ref。然而,我仍然感兴趣的是如何解决原始问题 - 或者为什么不能。 – Vladimir 2013-05-07 23:16:52