Reputation: 6046
I have a class which looks like this (simplified):
class DataObject {
public:
int getId() const;
const std::vector<int>& getAccounts() const;
};
What I need to have an associative array (hashmap) of the objects indexed by both id
and all the accounts it applies to. I.e. I need to lookup all the DataObject
instances which apply to a particular account (whereas a single DataObject
can apply to multiple accounts).
What I was thinking first was to use the boost::multi_index_contatiner
to index by both. However, I don't know how to create the index of all accounts for the object, and I'm not sure if that is even possible.
What I started with was this:
struct tags
{
struct id;
struct account;
};
typedef boost::multi_index_container<
const DataObject*,
boost::multi_index::indexed_by<
// index by the rule id (replacement, removal)
boost::multi_index::hashed_unique<
boost::multi_index::tag<tags::id>,
boost::multi_index::const_mem_fun<DataObject, int, &DataObject::getId>
>,
// index by the account
boost::multi_index::hashed_non_unique<
boost::multi_index::tag<tags::account>,
???
>
>
> DataMap_t;
And I don't know what I should put instead of the ???
to extract the single particular accounts. To recap, I do not want to index by the vector of accounts (which I'd know how to do), I need to index by each account in the vector.
So far I actually created a separate boost::unordered_map<int, const DataObject*>
index for the IDs and another separate boost::unordered_multimap<int, const DataObject*>
index for the account and there I iterate through all the accounts and store a value for each (and the same for the removal). But I'm wondering if there is a simpler way of doing that, preferably with the boost::multi_index_container
.
Upvotes: 3
Views: 783
Reputation: 393944
What you need is a relation table.
You could probably use Boost Bimap as well (seems easier to me):
The following code creates an empty bimap container:
typedef bimap<X,Y> bm_type; bm_type bm;
Given this code, the following is the complete description of the resulting bimap.
- bm.left is signature-compatible with std::map
- bm.right is signature-compatible with std::map
- bm is signature-compatible with std::set< relation >
Using Multi-Index I'd suggest storing the relations outside the objects:
struct Data { int id; };
struct Account { int id; };
struct Relation { int data_id, account_id; };
using Table = boost::multi_index_container<Relation,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct by_data>,
bmi::member<Relation, int, &Relation::data_id>
>
bmi::ordered_non_unique<
bmi::tag<struct by_account>,
bmi::member<Relation, int, &Relation::account_id>
>
>
>;
If you are careful about object lifetimes and reference stability, you could go fo a little more caller convenience:
struct Data { int id; };
struct Account { int id; };
struct Relation {
std::reference_wrapper<Data const> data;
std::reference_wrapper<Account const> account;
int data_id() const { return data.get().id; }
int account_id() const { return account.get().id; }
};
using Table = boost::multi_index_container<Relation,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct by_data>,
bmi::const_mem_fun<Relation, int, &Relation::data_id>
>
bmi::ordered_non_unique<
bmi::tag<struct by_account>,
bmi::const_mem_fun<Relation, int, &Relation::account_id>
>
>
>;
Upvotes: 4