我正在寻找从多索引容器中检索基于位字段的对象的最佳解决方案。为简单起见,数据为:
enum Bit
{
b0 = 1,
b1 = 2,
b2 = 4
};
struct Item
{
int field; // contains Bit values
int info;
};
据我所知,可能有两种可能性:
在equal_range调用中使用自定义谓词(此示例无效,因为我不知道如何定义谓词):
struct MyTag;
using boost::multi_index_container;
using namespace boost::multi_index;
typedef composite_key<Item,
member<Item, int, &Item::field>,
member<Item, int, &Item::info> > CompositeKey;
typedef hashed_non_unique< tag<MyTag>, CompositeKey> Index1;
typedef boost::multi_index_container<Item, indexed_by<Index1> > Container;
struct Predicate: public Index1::pred_type
{
/// what to put here?
};
void f()
{
Item item1 = { int( b0 | b1), 123};
Item item2 = { int( b1 | b2), 123};
Item item3 = { int( b2), 123};
Container c;
c.insert( item1);
c.insert( item2);
c.insert( item3);
auto result = c.get<MyTag>().equal_range( boost::make_tuple( int( b2), 123), Index1::hash_type(), Predicate());
for( auto i = result.first; i != result.second; ++i)
{
std::cout << i->field << ' ';
// expect item2 and item3
}
}
在枚举中为每个位添加多索引容器中的Item和索引中的访问器:
struct Item
{
int field;
int info;
bool isB0() const { return ( field & b0) != 0; }
bool isB1() const { return ( field & b1) != 0; }
bool isB2() const { return ( field & b2) != 0; }
};
struct MyTag1;
struct MyTag2;
struct MyTag3;
using boost::multi_index_container;
using namespace boost::multi_index;
typedef composite_key<Item,
const_mem_fun<Item, bool, &Item::isB0>,
member<Item, int, &Item::info> > CompositeKeyB0;
typedef composite_key<Item,
const_mem_fun<Item, bool, &Item::isB1>,
member<Item, int, &Item::info> > CompositeKeyB1;
typedef composite_key<Item,
const_mem_fun<Item, bool, &Item::isB2>,
member<Item, int, &Item::info> > CompositeKeyB2;
typedef hashed_non_unique< tag<MyTag1>, CompositeKeyB0> Index1;
typedef hashed_non_unique< tag<MyTag2>, CompositeKeyB1> Index2;
typedef hashed_non_unique< tag<MyTag3>, CompositeKeyB2> Index3;
typedef boost::multi_index_container<Item, indexed_by<Index1, Index2, Index3> > Container;
void f()
{
Item item1 = { int( b0 | b1), 123};
Item item2 = { int( b1 | b2), 123};
Item item3 = { int( b2), 123};
Container c;
c.insert( item1);
c.insert( item2);
c.insert( item3);
auto result = c.get<MyTag2>().equal_range( boost::make_tuple( true, 123));
for( auto i = result.first; i != result.second; ++i)
{
std::cout << i->field << ' ';
// expect item2 and item3
}
}
答案 0 :(得分:1)
我认为考虑这个问题的方法是:
&#34;如何在没有提升的情况下创建这些索引?&#34;
这将是这样的:
#include <unordered_map>
#include <list>
#include <functional>
#include <iostream>
enum Bit
{
b0 = 1,
b1 = 2,
b2 = 4
};
struct Item
{
int field;
int info;
bool isB0() const { return ( field & b0) != 0; }
bool isB1() const { return ( field & b1) != 0; }
bool isB2() const { return ( field & b2) != 0; }
};
template<int bit>
struct ItemHash
{
bool operator()(const Item& item) const {
auto accum = std::hash<int>()(item.info);
if ((item.field & bit) == bit)
accum += 1234567;
return accum;
}
};
template<int bit>
struct ItemEqual
{
bool operator()(const Item& l, const Item& r) const
{
auto lt = std::make_tuple((l.field & bit) == bit, l.info);
auto rt = std::make_tuple((r.field & bit) == bit, r.info);
return lt == rt;
}
};
struct Items
{
Item& insert(Item item)
{
// store object
auto i = _storage.insert(_storage.end(), item);
// update indeces
_by_b0.emplace(item, *i);
_by_b1.emplace(item, *i);
_by_b2.emplace(item, *i);
_by_b2_and_b1.emplace(item, *i);
return *i;
};
// object storage
std::list<Item> _storage;
// indexes we want to keep
std::unordered_map<Item, std::reference_wrapper<Item>, ItemHash<b0>, ItemEqual<b0> > _by_b0;
std::unordered_map<Item, std::reference_wrapper<Item>, ItemHash<b1>, ItemEqual<b1> > _by_b1;
std::unordered_map<Item, std::reference_wrapper<Item>, ItemHash<b2>, ItemEqual<b2> > _by_b2;
// multiple bits?
std::unordered_map<Item, std::reference_wrapper<Item>, ItemHash<b2|b1>, ItemEqual<b2|b1> > _by_b2_and_b1;
};
int main()
{
Item item1 = { int( b0 | b1), 123};
Item item2 = { int( b1 | b2), 123};
Item item3 = { int( b2), 123};
Items c;
c.insert( item1);
c.insert( item2);
c.insert( item3);
auto result = c._by_b2.equal_range( Item { b2, 123 });
for( auto i = result.first; i != result.second; ++i)
{
std::cout << i->second.get().field << ' ' << i->second.get().info << std::endl;
// expect item2 and item3
}
}
看着这一点,我们开始意识到field
中的这些位实际上是属性。我们真的想在每个属性组合上创建索引吗?只需将我们的Item
存储在vector
中,并在for循环中使用谓词,这真的会更有效,更容易维护吗?
例如:
#include <vector>
#include <algorithm>
#include <iostream>
#include <tuple>
enum Bit
{
b0 = 1,
b1 = 2,
b2 = 4
};
struct Item
{
int field;
int info;
bool isB0() const { return ( field & b0) != 0; }
bool isB1() const { return ( field & b1) != 0; }
bool isB2() const { return ( field & b2) != 0; }
};
struct matches_bits_and_info {
constexpr matches_bits_and_info(int bits, int info) : _bits(bits), _info(info) {}
bool operator()(const Item& item) const {
return std::make_tuple((item.field & _bits) , item.info)
== std::tie(_bits, _info);
}
int _bits, _info;
};
int main()
{
std::vector<Item> c;
c.push_back({b0 | b1, 123});
c.push_back({b0 | b2, 123});
c.push_back({b2, 123});
for (auto& item : c) {
if (matches_bits_and_info(b2, 123)(item)) {
std::cout << item.field << ' ' << item.info << std::endl;
}
}
}
预期结果:
5 123
4 123
为了使效率低于多索引容器解决方案(或手动解决方案),数据集必须大量。
答案 1 :(得分:1)
选项2非常好,实际上您的解决方案可以正常运行。对于选项1,无论你的Predicate
类有多聪明,都无法使它工作,解释如下:非唯一的散列索引组(意思是根据某些等价标准打包)元素,在您的情况下,field
和info
数据成员具有相同的值。让我们暂时忘记info
假装所有元素都有相同的值,并专注于field
:六个等价类(元素簇)是
0,1,2,3,4,5,6,7
,它们只是field
的三个比特0,1和2可以组合使用的不同值(这些聚类不一定按上面显示的顺序出现)。现在,如果您尝试执行equal_range
检索所有b2
元素,请设置您想要的操作,以返回下面粗体字母中显示的那些群集中的元素:
0,1, 2 , 3 ,4,5, 6 , 7
一般来说没有排列在一起,所以没有办法 equal_range
可以返回几个迭代器,范围超过这些和只有这些元素。
答案 2 :(得分:1)
可以考虑替代1,考虑到field
成员的可能值实际上并不是那么多(8),即逐个拾取 具有给定位集的相应field
值,请参阅以下示例中的for_each_value
:
<强> Live Coliru Demo 强>
#include <algorithm>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
enum Bit
{
b0 = 1,
b1 = 2,
b2 = 4
};
struct Item
{
int field;
int info;
};
struct MyTag;
using boost::multi_index_container;
using namespace boost::multi_index;
typedef composite_key<
Item,
member<Item, int, &Item::field>,
member<Item, int, &Item::info> > CompositeKey;
typedef hashed_non_unique< tag<MyTag>, CompositeKey> Index1;
typedef boost::multi_index_container<Item, indexed_by<Index1> > Container;
template<typename F>
F for_each_value(const Container& c,Bit b,int info,F f)
{
for(auto field:{0,1,2,3,4,5,6,7}){
if(field&b){
auto r=c.equal_range(std::make_tuple(field,info));
std::for_each(r.first,r.second,f);
}
}
return f;
}
#include <iostream>
int main()
{
Item item1 = { int( b0 | b1), 123};
Item item2 = { int( b1 | b2), 123};
Item item3 = { int( b2), 123};
Container c;
c.insert( item1);
c.insert( item2);
c.insert( item3);
for_each_value(c,b2,123,[](const Item& i){
std::cout << i.field << ' ';
});
}
<强>输出强>
4 6